INDEX
*  SPIRES Protocols
+  Introduction
1  Overview of Command Facilities
1.1  How Commands Are Used In Protocols
1.2  Introduction to Variables
1.3  Variable Substitution
1.4  Introduction to Functions
1.5  Commands for Input/Output
1.6  Commands for Testing Conditions
1.7  Commands Controlling Execution Flow in a Protocol
2  Invoking and Executing a Protocol
2.1  Executing a Protocol in the Active File: The XEQ Command
2.2  Invoking a Protocol from a Subfile: The XEQ FROM and .. Commands
2.2.1  (Nesting and Chaining Protocols)
2.3  Other XEQ Commands
2.4  Creating Execution Breaks: The BREAK XEQ and CONTINUE XEQ Commands
2.5  (*) Preloading Protocols for Efficiency
3  Controlling Execution Flow in a Protocol
3.1  Block Constructs for Structured Programming
3.1.1  BEGINBLOCK...ENDBLOCK
3.1.2  Looping Blocks: WHILE...ENDWHILE and REPEAT...UNTIL
3.1.3  Other Features of Block Constructs
3.1.4  Some Restrictions
3.2  Nesting and Branching Within Protocols
3.2.1  Executing Internal Procedures: The XEQ PROC Command
3.2.2  Tracing Execution: The SHOW XEQ STACK Command
3.2.3  The JUMP (or GOTO) Command
4  User-Defined Variables and Vgroups
4.1  Defining and Compiling a Global Vgroup
4.1.0  Defining a Local Vgroup
4.1.1  The Elements in a Vgroup Definition
4.1.2  (*) Statistics for Compiled Vgroups
4.2  Using Global Vgroups and Static Variables
4.2.1  Setting (Allocating) a Global Vgroup
4.2.2  Assigning Values to Static Variables
4.2.3  Examining the Values of Static Variables
4.2.4  Storing and Restoring Values
4.2.5  Destroying Vgroups or Stored Static Groups
4.3  Static Variable Arrays
4.4  (*) Details on Dynamic Variables
4.5  Passing Parameters Between System Components
4.5.1  The $SETPARMS and $GETPARMS functions
4.5.2  Passing Parameters by Reference
5  Protocol Commands and Syntax
5.1  Command Statements
5.1.1  Variable Value Assignment: The LET Command
5.1.1.1  (The Concatenation Operator)
5.1.2  Expressions With Mixed Type
5.2  The SET Command
5.3  Prompting the End-User for Input: The ASK Command
5.4  SET ASK
5.5  SET PROMPT
5.6  SET ECHO/NOECHO
5.7  ! Prefix
5.7a  : Prefix (The Colon Prefix)
5.8  Conditional Statements: The IF...THEN Command
5.8.1  The THEN and ELSE Commands
5.8.2  Comma Prefix
5.8.3  Percent Prefix, Unix Emulator
5.9  The SET MESSAGES (SET MES) Command
5.10  SET STOP/NOSTOP
5.10a  The SET XSTOP and SET NOXSTOP Commands
5.11  The WDSR Command
5.12  The WDSW, WDSE, and WDS Commands
5.13  The SET WDSR, SET WDSW, and SET WDST Commands
5.14  The "*" Command and the SHOW EVAL Command
5.15  PAUSE
5.16  The EVAL Command
5.17  Variable Substitution
5.18  Options on the SET TIMER Command: WAIT, PAUSE, and COUNTER
5.19  The WITHIN (WIT) Prefix
5.20  WITHIN LOG Prefix
5.20a  The WITH TIMELIMIT Prefix
5.21  (*) Commands for Monitoring Use of Memory
5.21.1  (*) The SHOW FREE CORE Command
5.21.2  (*) The SHOW SUBFILE MAP Command
5.22  The SET WIDTH Command
5.23  The WRITE FILE LOG Command
5.24  The CLEAR MESSAGE NUMBER Command
5.25  The SET SUBLATCH (SET SUBLAT) Command
6  System-Defined Variables
6.1  The STORE SETTINGS Command
6.2  Variable Types
6.2.1  (Flag Variables)
6.2.2  (Integer Variables)
6.2.3  (Character/String Variables)
6.2.4  (Line Variables)
6.3  Variables Classified by Function
6.3.1  Variables Used Primarily in Formats
6.3.2  Variables Allowed Only in User Defined Processing Rules
6.4  Variable Descriptions in Alphabetical Order
6.4.2  ($ACCOUNT)
6.4.3  ($ACTNUM ($ACTNO))
6.4.4  ($ASK)
6.4.5  ($AREANAME)
6.4.6  ($BIN)
6.4.8  ($CHRX and $CHRI)
6.5.0  ($CLEAR/$NOCLEAR ($CLR/$NOCLR))
6.5.2  ($COMMAND)
6.5.4  ($COMPXEQ)
6.5.6  ($CPUTIME)
6.5.7  ($CRTMODE)
6.5.8  ($CURCMD and $PRECMD)
6.5.9  ($DATE, $DATECC and $UDATE)
6.6.0  ($DEFQLOAD)
6.6.2  ($DELTA)
6.6.4  ($DIAG/$NODIAG)
6.6.8  ($ECHO/$NOECHO)
6.6.9  ($EDITOR (not applicable to Stanford environment))
6.6.9b  ($ELOGSCNT, $ELOGWCNT)
6.7.0  ($ELSE)
6.7.2  ($ENUM, $SNUM, and $PSNUM)
6.7.4  ($ENVIRON ($ENV))
6.7.6  ($ERRCODE ($MSGCODE, $MCODE))
6.7.7  ($ETIME)
6.7.8  ($FILEDATE and $FILETIME)
6.8.0  ($FILENAME)
6.8.2  ($FINDSTAT ($SRCHSTAT))
6.8.4  ($FORMAT)
6.8.6  ($FORTYPE ($FORTYP))
6.8.8  ($FRAME)
6.9.0  ($GETSPATH/$GETXPATH)
6.9.2  ($GLOFORMAT)
6.9.4  ($GOALREC)
6.9.6  ($GXCOUNT, $GPCOUNT, $PXCOUNT, and $PPCOUNT)
6.9.7  ($IDATA (not applicable to Stanford environment))
6.9.8  ($INFOX and $INFOI)
6.9.9  ($INTXEQ)
6.10.0  ($IOCOUNT)
6.10.1  ($JOBNUM)
6.10.2  ($KEY)
6.10.3  ($LASTRESNUM)
6.10.4  ($LASTXEQ ($XEQLAST))
6.10.6  ($LENGTH ($LEN))
6.10.8  ($LIST/$NOLIST)
6.11.0  ($MAIL)
6.11.2  ($MSGINT)
6.11.4  ($MSGLEV)
6.11.6  ($MSGLIT)
6.11.8  ($MSGNUM ($MNUM))
6.12.0  ($MULTILOG variable)
6.12.2  ($NAME)
6.12.4  ($NEXTSLOT and $NEXTSLOTKEY)
6.12.6  ($NEXTWDSR)
6.12.8  ($NEXTWDSW)
6.12.8.1  ($NO)
6.13.0  ($NOLEV ($NOL))
6.13.1  ($ODATA (not applicable to Stanford environment))
6.13.1a  $PARM
6.13.1b  ($PARMCNT)
6.13.2  ($PATHCUR)
6.13.3  ($PATHKEY)
6.13.4  ($PATHNUM)
6.13.6  ($PROGRAM)
6.13.8  ($PROMPT)
6.13.9  ($PROXYID, $PROXYNAME, $PROXYPHONE, $PROXYEMAIL)
6.14.0  ($PRTCNT)
6.14.2  ($PRTLVL)
6.14.6  ($RECSEP)
6.14.8  ($RELPOS)
6.15.0  ($RESCNT)
6.15.1  ($RESHIST)
6.15.2  ($RESNAME)
6.15.3  ($RESNUM)
6.15.4  ($RESULT)
6.15.6  ($RETCODE (not applicable to Stanford environment))
6.15.7  ($SEARCHMOD)
6.15.8  ($SEED)
6.16.0  ($SELCHR, $DISCHR, and $MINCHR)
6.16.2  ($SELECT ($SEL))
6.16.4  ($SELECTED)
6.16.6  ($SEP)
6.16.8  ($SERX and $SERI)
6.17.0  ($SETFORMAT)
6.17.1  ($SITE)
6.17.2  ($SLOT/$NOTSLOT)
6.17.3  ($SORTCODE)
6.17.4  ($STACK)
6.17.6  ($STOP/$NOSTOP)
6.17.8  ($SUBCODE)
6.18.0  ($SUBFSIZE)
6.18.2  ($SUBLATCH)
6.18.4  ($SUPERMAX and $SUPERVAL)
6.18.5  ($SYSTYPE)
6.18.6  ($TCOMMENT)
6.18.8  ($TERMINAL ($TERM, $TER))
6.19.2  ($TERMTYPE ($TTYPE))
6.19.4  ($TIME and $UTIME)
6.19.6  ($TIMELIMIT)
6.19.8  ($TIMER)
6.20.2  ($TRACE/$NOTRACE)
6.20.4  ($TRANSFER ($TRA))
6.20.6  ($TRUE/$FALSE)
6.20.7  ($UNIVID, $UNIVNAME, $UNIVPHONE, $UNIVEMAIL)
6.20.8  ($UPDTYPE)
6.21.2  ($UPPER/$UPLOW)
6.21.4  ($USER ($USE) and $GCODE ($GRO))
6.21.6  ($UCODE ($USERCODE, $UCD))
6.21.8  ($USERNAME variable)
6.22.2  ($VERSION)
6.22.4  ($WARNX and $WARNI)
6.22.6  ($WDSL ($WDSLINE))
6.22.8  ($WDSR)
6.23.2  ($WDST)
6.23.4  ($WDSW)
6.23.6  ($WIDTH ($WID))
6.23.8  ($XEQ)
6.23.9  ($XEQLVL)
6.24.1  ($XTRACE)
6.24.2  ($YES/$NO)
6.24.4  ($ZRESULT/$NZRESULT ($ZRES/$NZRES))
7  Functions
7.1  Overview
7.1.1  Functions for Variable Data Type Conversion (Summary)
7.1.2  String Manipulation Functions (Summary)
7.1.3  Variable Manipulation Functions (Summary)
7.1.4  Functions for Numeric and Packed Decimal Manipulation (Summary)
7.1.5  Functions for Subfile and Data Value Information (Summary)
7.1.6  Functions for Date and Time Manipulation (Summary)
7.1.7  Other Miscellaneous Functions (Summary)
7.2  Function Descriptions in Alphabetical Order
7.2.0b  (The $ABS Function)
7.2.0c  (The $ACCESSTEST Function)
7.2.1  (The $AMATCH Function)
7.2.2  (The $APMATCH Function)
7.2.2a  (The $ARMATCH Function)
7.2.2b  (The $ASCII Function)
7.2.3  (The $ASET Function)
7.2.3.B  (The $BLDLCTR and $UBLDLCTR functions)
7.2.3.D  (The $BNF Semantics)
7.2.3a  (The $ASORT and $ASORTX Functions)
7.2.3b  (The $AUTHPARMS Function)
7.2.3c  (The $BNF Function)
7.2.4  (The $BREAK Function)
7.2.4a  (The $CAPITALIZE Function)
7.2.5  (The $CASE Function)
7.2.6  (The $CHANGE Function)
7.2.6a  (The $CHANGELIST Function)
7.2.7  (The $CHARACTER Function)
7.2.7a  (The $CHECK Function)
7.2.7b  (The $CLRSUBF Function)
7.2.7c  (The $COLUMNTEST Function)
7.2.8  (The $COMPARE Function)
7.2.9  (The $DATEIN Function)
7.2.9.1  (The $DATEOUT Function)
7.2.9.2  (The $DAYS Function)
7.2.9.3  (The $DATETIME Function)
7.2.9.4  (The $DATEOUT Function for datetime)
7.3.1  (The $DECIMAL Function)
7.3.2  (The $DEFQTEST Function)
7.3.2a  (The $DENCODE Function)
7.3.3  (The $DOUBLE Function)
7.3.4  (The $DYNASET Function)
7.3.4a  (The $DYNAZAP Function)
7.3.5  (The $DYNGET, $DYNPUT, and $DYNZAP Functions)
7.3.6  (The $EDIT Function)
7.3.7  (The $ELEMINFO and $ELIDINFO Functions)
7.3.8  (The $ELEMTEST, $ELNOTEST and $ELIDTEST Functions)
7.3.8a  (The $ENCIPHER Function)
7.3.8b  (The $ENCODE and $DECODE Functions)
7.3.9  (The $EVALUATE Function)
7.4.1  (The $EXP Function)
7.4.1b  (The $FACTORIAL Function)
7.4.1c  (The $FACTORS Function)
7.4.2  (The $FLAG and $NOT Functions)
7.4.2a  (The $FRAMETEST Function)
7.4.2b  (The $GETELOG Function)
7.4.2c  (The $GETPARMS Function)
7.4.3  (The $GETxVAL Functions: $GETUVAL, $GETCVAL, $GETIVAL, $GETXVAL)
7.4.3a  (The Structural Occurrence Map with the $GETxVAL Functions)
7.4.3b  (The $GETVMATCH Function)
7.4.3c  (The $GETVOCC Function)
7.4.4  (The $HEX Function)
7.4.5  (The $INDEXINFO Function)
7.4.5.1  (The $INDEXNUM Function)
7.4.5.2  (The $INDEXTERM Function)
7.4.5a  (The $INSERT Function)
7.4.6  (The $INSETL, $INSETR, and $INSETC Functions)
7.4.7  (The $INTEGER Function)
7.4.8  (The $ISSUECMD Function)
7.4.8a  (The $ISSUEMSG Function)
7.4.9  (The $LEFTSTR Function)
7.5.1  (The $LEFTSUB Function)
7.5.2  (The $LINE Function)
7.5.3  (The $LOG Function)
7.5.4  (The $LOOKSUBF and $LOOKSUBG Functions)
7.5.4a  (The $LOOKSYS Function)
7.5.5  (The $MATCH Function)
7.5.6  (The $MOD Function)
7.5.7  (The $NEGATIVE Function)
7.5.8  (The $NOLF Function)
7.5.8b  (The $NORMALIZE Function)
7.5.9  (The $PACKED Function)
7.6.1  (The $PACKTEST Function)
7.6.2  (The $PARSE and $PARSESTRIP Functions)
7.6.2a  (The $PATHFIND and $PATHINFO Functions)
7.6.3  (The $PMATCH Function)
7.6.4  (The $PRECISION and $WINDOW Functions)
7.6.4a  (The $PRISMINFO Function)
7.6.5  (The $PROCSUBG Function)
7.6.6  (The $RANDOM Function)
7.6.7  (The $REAL Function)
7.6.7a  (The $RECINFO Function)
7.6.8  (The $RECTEST Function)
7.6.8a  (The $REF Function)
7.6.9  (The $REMAINDER Function)
7.7.0  (The $RESINFO Function)
7.7.1  (The $RETYPE Function)
7.7.1b  (The $REVERSE Function)
7.7.2  (The $RIGHTSTR Function)
7.7.3  (The $RIGHTSUB Function)
7.7.3a  (The $RMATCH Function)
7.7.3b  (The $ROOT Function)
7.7.3c  (The $RSTRIP and $LSTRIP Functions)
7.7.4  (The $SEARCHINFO Function)
7.7.4a  (The $SEARCHTEST Function)
7.7.4b  (The $SET Function)
7.7.4c  (The $SETPARMS Function)
7.7.5  (The $SIZE Function)
7.7.6  (The $SPAN Function)
7.7.7  (The $SQRT Function)
7.7.8  (The $SQU Function)
7.7.9  (The $SSW Function)
7.8.0  (The $STATGET and $STATPUT Functions)
7.8.1  (The $STRING Function)
7.8.2  (The $STRIP Function)
7.8.3  (The $SUBSTR Function)
7.8.3a  (The $SYSEVAL Function)
7.8.3b  (The $SYSINFO Function)
7.8.4  (The $SYSTEM Function)
7.8.5  (The $TEST Function)
7.8.6  (The $TIMEIN and $TIMEOUT Functions)
7.8.6b  (The $TRANINFO Function)
7.8.7  (The $TRANSLATE Function)
7.8.7.1  (The $TRIM Function)
7.8.8  (The $TRUNC Function)
7.8a  (The $TYPE Function)
7.9.1  (The $TYPETEST Function)
7.9.1.1  (The $UNEDIT Function)
7.9.1.1a  (The $VARGET and $VARPUT Functions)
7.9.1.2  (The $VARTEST Function)
7.9.1.2b  (The $VERIFY Function)
7.9.1.3  (The $VGROUPALTER Function)
7.9.2  (The $VGROUPINIT Function)
7.9.3  (The $WDS Function)
7.9.3.1  (The $WINDOW Function)
7.9.3a  (The $WORKDAYS Function)
7.9.3b  (The $XDATE Function)
7.9.4  (The $XEQSTACK Function)
7.9.5  (The $XSTR Function)
7.9.5a  (The $XSUB Function)
7.9.5b  (The $YYCALC Function)
7.9.5c  (The $YYTEST Function)
7.9.6  (The $ZAP Function)
7a  System Variables and Functions Associated With Triples
7a.1  (The $MAKE Function)
7a.2  (The $NEW Variable)
7a.3  (The $ANY Variable)
7a.4  (The $MADE Function)
7a.5  (The $UNMAKE Function)
7a.6  (The $UNMAKETRIPLE ($UNMAKETRI) Function)
7a.7  (The $LOOKUP Function)
7a.8  (The $ATTRIBUTE ($ATTR), $OBJECT ($OBJ), and $VALUE Functions)
7a.9  (The $GROUP ($GRP) Function)
7a.10  (The $GROUPSIZE ($GRPSIZE, $GRPSZ) Function)
7a.11  (The $GROUPSORT ($GRPSORT) Function)
7a.12  (The $GROUPELEMENT ($GROUPELEM, $GRPELEM) Function)
7a.13  (The SHOW TRIPLES (SHO TRI) and CLEAR TRIPLES (CLE TRI) Commands)
7a.14  (Using Triples -- Some Guidelines and Examples)
8  Protocol Debugging
8.1  Diagnostics
8.2  Display and Modification of Variable Values
8.3  Ascertaining the XEQ Level
8.4  Protocol and Command Execution Tracing
8.4.1  Protocol Tracing: SET XTRACE
8.4.2  Logging for Protocol and Format Tracing: SET TLOG
8.4.3  Error Logging: SET ELOG
8.5  Protocol Testing in Command Mode
9  Building a Protocols File
9.1  The PERFORM BUILD PROTOCOLS Command
9.2  Using PUBLIC PROTOCOLS
9.3  Formatting Protocols Code: The PFORMAT Command
10  Compiling a Protocol
10.1  Compiling a Protocol: Overview
10.2  Compiling Protocols
10.2.1  Zapping Protocols
10.3  Executing Compiled Protocols: The SET COMPXEQ Command
10.4  Efficient Execution: Coding Strategies
10.5  Variable Communication: Local and Global Vgroups
10.6  Executing Compiled and Non-Compiled Protocols Simultaneously
10.7  (*) Statistics for Compiled Protocols
10.8  Compiling a Protocol the Old Way
10.9  Converting from the Old to the New Way of Compiling Protocols
11  Protocol Examples
11.1  (PERFORM)
11.2  (PAGINATE)
11.3  (CLEAR.TEXT)
11.4  (LIST.PROTOCOL)
11.5  (SHOW.FILE.PERMITS)
11.6  (SAVE.SETTINGS)
11.7  (RESTORE.SETTINGS)
11.8  (PAGE.TO.SECTION)
12  Protocol Exercises
12.1  (Using SPIRES system variables in a command)
12.2  (Executing a protocol stored in the active file)
12.3  (Printing the values of variables at the terminal)
12.4  (Substitution versus Evaluation)
12.5  (Using the ASK command to prompt for information from the terminal)
12.6  (Using the WDSW command)
12.7  (Using the ASK command with label statements and JUMP commands)
12.8  (Using the IF command)
12.9  (Using XEQ command options with a protocol in the active file)
12.10  (Choosing a protocol subfile)
12.11  (Using protocols in the PUBLIC PROTOCOLS subfile)
12.12  (Listing protocols in the PUBLIC PROTOCOLS subfile)
12.13  (Establishing your own protocol subfile)
12.14  (Adding protocols to your subfile and executing them)
12.15  (Displaying and modifying protocols in your protocol subfile)
12.16  (Establishing a SPIRES entry protocol)
:  Appendices
:1  Error Messages for VGROUP Compilation
:4.2.1.6.1  * LONG VARIABLE HAS OCC > 1
:4.2.1.6.2  * BAD LEN FOR VARIABLE TYPE
:4.2.1.6.3  * VARIABLE SUBSCRIPTING PROBLEM
:4.2.1.6.4  * UNKNOWN VARIABLE TYPE
:4.2.1.6.5  * VGROUP TOO LARGE
:4.2.1.6.6  * VGROUP VALUE CONVERSION ERROR
:4.2.1.6.7  * VGROUP VALUE ERROR
:4.2.1.6.8  * VALUE INCONSISTENT WITH MULTIPLE OCCS
:4.2.1.6.9  * VALUE MUST BE INTEGER
:4.2.1.6.10  * MULTIPLE OCC VALUE MUST BE INDEXED
:4.2.1.6.11  * INCONSISTENT STRING REDEFINITION
:4.2.1.6.12  * REDEFINITION LOOPING PROBLEM
:4.2.1.6.13  * ILLEGAL USAGE WITH DYNAMIC TYPE
:4.2.1.6.14  * VARIABLE TABLE OVERFLOW
:4.2.1.6.15  * REDEFINED VARIABLE CANNOT HAVE VALUE
:29  SPIRES Documentation

*  SPIRES Protocols

******************************************************************
*                                                                *
*                     Stanford Data Center                       *
*                     Stanford University                        *
*                     Stanford, Ca.   94305                      *
*                                                                *
*       (c)Copyright 1994 by the Board of Trustees of the        *
*               Leland Stanford Junior University                *
*                      All rights reserved                       *
*            Printed in the United States of America             *
*                                                                *
******************************************************************

        SPIRES (TM) is a trademark of Stanford University.

+  Introduction

What is a Protocol?

A protocol is a series of SPIRES and/or system commands, gathered together into a single data set or record, and generally devoted to accomplishing a single task or a series of closely related tasks.

Usually you store a protocol as a record in a SPIRES protocol subfile. [See 9.] Once you have set the protocol subfile for execution, with either the SET XEQ or the SET COMPXEQ command [See 2.2, 10.3.] you can cause a protocol to execute by issuing a single command:

  ..protocolname            <--or XEQ FROM protocolname

For example, to execute a protocol called "Print.Report" stored in the protocol subfile "ProtoFile":

  -> set xeq protofile
  -> ..print.report

"PrintReport" might consist of a few commands or a hundred commands -- in either case, the entire protocol is set in motion by the above commands. [You can also execute a protocol by placing it in your active file and issuing the XEQ command.]


Contents and Structure of a Protocol

A protocol is mostly made up of the same sorts of commands that you might issue in an online session, in "command" mode. Thus the commands issued interactively on the left below can also be issued within a protocol, as shown on the right:

  (Commands Online)                    (Sample Protocol)
  -------------------------------      ----------------------------
  -> select drinks                     * SAMPLE.PROTO
  -> set format display                ++DISPLAY
  -> for tree                          Select Drinks
  +> in active continue display 5      Set Format Display
                                       For Tree
                                       In Active Continue Display 5
                                       Return

In addition to the familiar set of interactive commands, protocols provide ways to nest to subroutines [See 1.7, 3.2.1.] or to execute commands only if certain conditions are met [See 1.6, 3.1.2, 5.8.] or to invoke one protocol from another in "nested" execution [See 2.2.] and so on. Many of these special facilities are not available interactively, but only as protocol statements (or as Uprocs in other SPIRES components such as formats).


Uses and Benefits of Protocols

What are protocols for? Though protocols can be used to accomplish any task involving series of commands, usually they're designed to work in close cooperation with other Prism or SPIRES features in order to customize a complete application. For instance, a protocol can interact with a SPIRES format by issuing the SET FORMAT command to set the format, later issuing commands that execute frames within the format or that use its frames to display and update records.

There are many advantages to accomplishing such tasks through protocol control, rather than doing them interactively: one big advantage is that end-users don't have to know the syntax of commands that a protocol executes on their behalf. This can provide greater comfort for an application's users while simultaneously strengthening the security of that application's data.


Labels and Procs

The basic skeleton of a protocol might be pictured like this:

  * Key            <--record-key of protocol -- the protocol's "name"
     :
  ++Label          <--label statement
  Command
  Command          <--virtually any command
     :
  Return           <--return to previous level, e.g., from protocol
                      to command level

In the diagram above, everything from "++Label" down to "Return" could be seen as a single Proc (i.e., a subroutine or self-contained procedure). A protocol is often structured in a number of separate labelled Procs along this model -- in fact, protocols for Prism are always structured in self-contained and labelled Procs. In protocols outside Prism, labels and Procs are optional, but they can help clarify the program's structure as well as providing a destination for a nesting or branching command like XEQ PROC or JUMP:

  * TEST.PROTO
      :
  Xeq Proc SELECT.FILE  <--Causes protocol to nest to Proc named SELECT.FILE
  Show Eval 'Testfile is now selected.'
  Xeq Proc SEARCH
      :
  Return
      :
  ++SELECT.FILE
  Select Testfile
  Set Format Test
  Return              <--Causes protocol to "return" to previous level,
      :                   and execute SHOW EVAL command above

Thus Procs help structure a protocol into subprograms, though (given the complex ways protocols interact with other tools such as Prism or formats), the structure of a protocol is rarely as tidy as in the example above. In any case, though the natural bent of a protocol is to execute commands from top down, you'll find numerous tools in upcoming sections to help control execution flow more precisely.


What's to Come

The rest of this document covers the following topics:

Much of the material covered in this document also applies, in most details, to procedural code in other SPIRES components: i.e., Uprocs in SPIRES formats, and Uprocs within file definition Userprocs. For information on procedural code in these components, see the manuals "SPIRES Formats" and "SPIRES File Definition". For information on protocols in Prism, see the document "Prism Applications".

1  Overview of Command Facilities

This chapter provides an overview of the command facilities most useful within a protocol. Many of these commands and procedures can be used, sometimes in slightly different form, as Uprocs within formats and file-definition Userprocs as well.

After discussing general uses for protocol commands, the chapter goes on to provide:

1.1  How Commands Are Used In Protocols

A protocol in SPIRES can contain virtually any SPIRES command that can be issued interactively (i.e., in "command mode"), and nearly any WYLBUR (or ORVYL or MILTEN) command that can be issued in an interactive, line-by-line session.

The forms and options of these commands are the same under protocol control as in "command" mode. Note that access to WYLBUR and ORVYL commands means you can use and save data sets, collect text, and so on, all under protocol control.

Protocols To Control the End-User's Environment

Since virtually any interactive command can be issued within a protocol, you can use a protocol to execute a long and repetitive or technical series of commands. The protocol can also establish a specially-controlled environment (perhaps with security features) to modify and/or simplify the end-user's view of his or her environment.

For example, the simple Prism protocol below, executed when the file is selected, uses the SET SEARCH MODIFIER command so that only "Public" records are retrieved in a search, and uses SET FILTER so that only date values of 1986 are displayed:

  * FILE
  ++SELECTION
  Set Search Modifier and Public = Yes
  Set Filter for Date Where Date = 1986
  Return

End-users never need to learn the complicated syntax of, say, SET FILTER, since the protocol executes the command on their behalf. An additional advantage is that end-users may not be able to circumvent the security provided by the commands. [Since protocols can issue most SPIRES commands, some commands mentioned in passing in this document are covered in detail elsewhere: for instance, filters are discussed in "SPIRES Technical Notes", SET SEARCH MODIFIER in "SPIRES Searching and Updating".]

Constructing Commands for the End-User

In addition to issuing sequences of literal commands, protocols can "construct" complex commands based on an end-user's responses to a simpler set of prompts or questions. In protocols written for SPIRES, you usually use the ASK command to prompt for end-user input, [See 1.6, 5.3.] then construct your command using this response.

In Prism, you never use ASK (since Prism handles end-user prompting), but you still might construct a command based on user input. For instance, a file of restaurants might install a search type called FRENCH, supported by a "protocol step" that executes when an end-user requests a search such as FIND FRENCH. The protocol step could perform the full search behind the scenes:

  * FILE
    :
  ++FRENCH.FOOD
  Perform Prism Search Cuisine = French or Country = France
  Return

In this case, the protocol helps a user benefit from SPIRES indexes without having to name the index or even know it exists. Prism and the protocol construct the search -- the user just answers a prompt or two. [See the "Prism Applications" document for details on PERFORM PRISM SEARCH.]

You can use protocols for your own convenience as well as your end-user's. For instance, if you always issue the same set of commands when you enter SPIRES, you can assemble the commands in a protocol and add it, with your account as key, to the ENTRY COMMANDS subfile. The protocol will execute automatically whenever you call SPIRES. [See the "SPIRES Searching and Updating" manual for details.]

For instance, below the user GQ.PRO below uses ENTRY COMMANDS to establish a personal protocol subfile for execution, and accomplish a few other tasks: [See 2.2 for SET XEQ, 10.3 for SET COMPXEQ for compiled protocols.]

  * GQ.PRO
       :
  Set Xeq My Protofile      <--or Set Compxeq for compiled protocols
  Set Messages 4            <--for automatic explanatory text
  Return

The next few sections discuss variables, system functions, and other useful protocol facilities.

1.2  Introduction to Variables

Variables in the broadest sense are reserved places where you or the system can store and access information. As you'll see, one of the most powerful features of a protocol is the way it can access and store this "variable" information.

Variables in SPIRES can be divided into two broad categories, system-defined variables and user-defined variables, which we'll discuss in turn. In addition, this section briefly introduces two important commands discussed in more detail later in the manual:

  Command to Assign      Purpose
  Value to Variable      of command
  -------------------    -------------------------------
  SET                    Assign value to certain system
                         variables (or set a condition)

  LET                    Assign value to user variable

System Variables

System variables are variables defined and maintained by SPIRES, such as $DATE (which holds the current date) or $RESULT (which represents the number of records in a current search result).

With some system variables, you can both access the value and set it, using the SET command: for instance, SET LENGTH [See 5.2.] resets the value of the system variable $LENGTH:

  -> set length 68
  -> show eval $length
  68

(If you have SET WYLBUR, prefix the SET LENGTH command with a !, so Wylbur will pass the command to SPIRES.)

In other cases, you can access the value of a system variable but not change it -- for instance, you could not reset $DATE.

The values of some system variables change as a result of command execution: for instance, a failed command sets $NO, and selecting a subfile sets $SELECTED. In cases like these, accessing the variable's current value can give you important information about the status of your end-user's current session. [See 6 for more information on system-defined variables.]

Example

For example, the line from a Prism protocol below tests $NO to see if the preceding command failed. If the command fails, the protocol sets the value of the Prism variable $STATUS to 'STOP', which tells Prism to discontinue processing the command sequence (e.g, selecting a file). The protocol also sets the variable $MSGLINE with a message to display to the end-user:

  ++CHECK.AUTH
     :
  - (Preceding lines check user's authorization)
  If $No Then Set MsgLine = 'Please check authorized code and try again.'
  Then Set Status = 'STOP'
  Return

User Variables

Besides system variables, SPIRES offers the facility of user-defined variables, which you define yourself. One common way to assign a value to a user-defined variable is to use the LET command [See 5.1.1.] as in the example below:

  Let CurrTime = $Time     <--assigns value of $TIME system variable
                              to the user variable CurrTime

As a system variable is generally prefixed by a dollar sign, a user variable is generally prefixed by a pound sign (#). Actually, to be more precise, when you're referring to a user variable's value you prefix its name with a '#'; when you're referring to its name you leave the '#' off:

  Let LineNum = #LastLine  <--"Assign the VALUE of the variable named
                               LastLine to the variable named LineNum"

If you ever need to use a pound sign in a statement that would otherwise be evaluated (with variable substitution) you can either put the pound sign in quotation marks to show it is a string, or, if that's not possible, double it.

Dynamic and Static User Variables

User-defined variables themselves come in two different flavors: dynamic variables and static (that is, pre-compiled) variables.

Dynamic variables are user variables that you define just by naming them and using them in an assignment statement such as a LET statement. [Dynamic variables can also be explicitly defined in a variable group or "vgroup" or in a file definition's Userproc, where they can be extremely useful. [See 4.4]] Dynamic variables are handy for quick or small-scale tasks, but for the most efficient use of system memory, you should rely primarily on static variables within a production application.

Static variables are user variables that you predefine and store together in a vgroup (variable-group), usually (for a protocol) in the system subfile VGROUPS. When your application needs to call on these static variables, your code can first allocate them as a group (using the SET VGROUP command), utilizing computer memory more efficiently than dynamic variables do. [See 4 and the following sections for complete details on defining and compiling vgroups.]

The Data Type of Variables

Variables can be of several data types: string, integer, flag, line, real, and several others. In manipulating user variables, it can be important to declare or confirm a variable's type in order to avoid conversion errors. [See 5.1.2.] For static variables, data type is generally part of the variable's definition within the vgroup. For dynamic variables, you can often confirm data type by using a SPIRES system function. [See 1.4, 7.]

The SHOW EVAL Command and Variables

To display a variable's value online, you can use the SHOW EVAL command, as in the example below:

   -> show eval $date             <---(displays a system variable's value)
   09/16/86
   -> let currentdate = $date
   -> show eval #currentdate      <---(displays a user variable's value)
   09/16/86

The SHOW EVAL command has other uses as well. [See 5.14 for details on the command.]

1.3  Variable Substitution

When a SPIRES or WYLBUR command contains a user-defined variable or a system variable, you'll sometimes want to force variable substitution before you pass the command on to be parsed and executed. (In "variable substitution" the variable's name, as it appears in the command prefixed with # or $, is replaced with the variable's current value, converted to a string.)

The way to force variable substitution in a command is to prefix the command with a slash ('/'), as in the example below:

  -> select paperbacks
  -> set ask updike
  -> /find author $ask
  -Result: 10 TITLES

The slash before the FIND command causes the value "updike" to be substituted for the system variable $ASK before the FIND command is actually executed. [The slash prefix is not allowed in formats or file-definition Userprocs.]

An important exception: there are four SPIRES commands -- IF, LET, EVAL, and SHOW EVAL -- that almost never use the slash prefix, because variable substitution occurs automatically with these commands. [See 5.1.1, 5.8, 5.17.]

The example below suggests when a slash prefix is needed and when not:

  -> show line abc              <---No variable is involved, so no
    126 GQ.ABC   .....              slash is necessary

  -> / show line $user          <---The slash is necessary to force
    126 GQ.ABC   .....              evaluation of the $USER variable

  -> show eval $user            <---Despite the variable, no slash is
  ABC                               necessary with SHOW EVAL

For more information about variable substitution, EXPLAIN VARIABLE SUBSTITUTION online or see the more detailed section later in this document. [See 5.17.]

1.4  Introduction to Functions

SPIRES offers a number of useful system functions for performing predefined operations on a value that you supply. Most system functions take the "input value" that you supply and return a second value that is the result of the function's operation. For instance, $CAPITALIZE('abc def') takes the input value 'abc def', and as a result of the function's processing returns the value 'ABC DEF'. The $SIZE function processing the same value returns the length of the value:

  -> show eval $capitalize('abc def')
  ABC DEF
  -> show eval $size('abc def')
  7

By the way, delimiter characters (such as the apostrophes above), can have a significant effect on how a function processes an input value. [See 7.1.] Without delimiters, the blank space in the input value below is ignored:

  -> show eval $capitalize(abc def)
  ABCDEF

There are several basic kinds of functions, including string manipulation functions and functions to convert the data type of variable values. [See 7 for a more detailed overview and alphabetical listing of functions.] The small guided tour below demonstrates only a few of the many functions available, namely $LEFTSTR (or $LSTR), $TYPE, and $INTEGER (or $INT):

  -> show eval $time
  15:47:40

  Use the $LEFTSTR function to return a "substring"
  based on the first two characters of $Time

  -> show eval $leftstr($time,2)
  15

  Store the value in a user variable:

  -> let hour = $leftstr($time,2)

  Use the $TYPE function to determine the data type
  of #hour:

  -> show eval $type(#hour)
  STR

  Convert Hour to have a data type of "integer"
  using the $INTEGER function:

  -> let hour = $integer(#hour)
  -> show eval $type(#hour)
  INT

Using A Function to Check or Convert Data Type

The functions for variable data type conversion [See 7.1.1.] are often important for converting a value's "data type" to a different type -- or checking to make sure the input value is legally convertible to a needed data type. For instance, to ensure that the value of the dynamic variable Number is legally convertible to an integer (and isn't, say, a decimal fraction):

  If $TypeTest(#Number,INT) ~= 'INT' then * (Number is not an integer)

1.5  Commands for Input/Output

This section provides a very brief overview of commands available to protocols for input and output. Two things to note: 1) many input/output tasks involving data base access are handled better by formats than by protocols; 2) some command facilities described below are not needed for Prism development (e.g., you would rarely or never need to use ASK or SHOW EVAL in a Prism application).

  Input/Output Command           Purpose
  ----------------------------   ---------------------------------
  ASK                            Prompt user for input at terminal
  WDSR                           Read line from active file
  WDSW, WDSE, IN ACTIVE prefix   Write to active file
  SHOW EVAL, * command           Display text at the terminal

To prompt your user for input at the terminal, you can use the ASK command. The user's response is stored in the variable $ASK, where you can test it; you can also have the command take a special response if the user only presses the RETURN or the ATTN/BREAK key:

  ASK PROMPT='Please enter your name' NULL='Jump Retry' ATTN='Jump Exit'

Here the user will be prompted with the string 'Please enter your name'; the protocol will JUMP to a label group labelled "Retry" if the user presses RETURN, and will jump to the label group "Exit" if the user presses BREAK. [See 5.3 for details on the ASK command, 3.2 for label groups, 3.2.3 for the JUMP command.]

As mentioned above, the ASK command is not used in Prism, which handles prompting of the end-user itself.

To read a line from the logged-on user's active file (rather than from the terminal), you can use the WDSR command. [See 5.11.] Again the value will be stored in $ASK.

To display a message to the terminal, you can use the SHOW EVAL command or the * command. [See 5.14.] For instance, the ASK command above might be followed by this command:

  SHOW EVAL 'Your name is ' $ASK

Note that SHOW EVAL evaluates variables such as $ASK automatically, but literal strings ('Your name is ') should be surrounded by apostrophes.

To write to the active file, you can use the WDSW or WDSE commands [See 5.12.] or the more versatile IN ACTIVE prefix, as in the example below:

  ++DISPLAY
  /Wdse Records added on $Date
  For Adds
  In Active Continue Display All, End = 'Jump Print'
    :

[See "SPIRES Searching and Updating" for more on the IN ACTIVE prefix.]

In addition to the input/output commands mentioned above, SPIRES offers methods for transmitting data to or from several other areas or devices besides the active file or the terminal. For a detailed discussion, see the manual "SPIRES Device Services".

1.6  Commands for Testing Conditions

Condition testing is a basic feature not only in protocols but also in formats and Userprocs. One common way to test a condition in SPIRES is to use the three basic statements IF, THEN, and ELSE:

  Condition Testing Command   Purpose
  -------------------------   ----------------------------------
  IF                          Test condition(s) following IF
  THEN                        Execute if condition is true
  ELSE                        Execute if condition is false

If the condition you name after IF is true, then subsequent commands prefixed by THEN are executed -- if the IF condition is false, then subsequent commands prefixed by ELSE are executed.

   If $Result Then Xeq Proc Result
   Else Xeq Proc NoResult

Actually, you can almost think of IF...THEN as a single command-complex, in the sense that an IF clause must always be followed by a THEN clause and THEN must always be preceded by IF. [You can use a colon in place of THEN: If $Result : Xeq Proc Result.] Likewise, ELSE is never used except after a preceding IF...THEN. For example, the statement below is incorrect because no THEN statement has been coded:

   If $Result Xeq Proc Result       <--invalid command

In addition to these condition commands, the block constructs REPEAT...UNTIL and WHILE...ENDWHILE also test conditions. These constructs are discussed in the following section. [See 1.7.]

What is a "condition"? A condition can be a single term, such as a flag variable whose value is either "1" (for TRUE, or ON) or "0" (for FALSE, or OFF):

  If $Selected Then...       <--(The $SELECTED system variable tells
                                whether or not a subfile is selected.)

It might also be a comparison of two terms (e.g., a comparison of two numbers):

  If #Total = 0 Then...

The condition might even be compound:

  If #Number > 0 Or #Number < 20 Then...

For a fuller description of the different kinds of conditions, see the later section on IF...THEN [See 5.8, 5.8.1 for a discussion of ELSE.]

Example of IF...THEN

In the following example, the user variable Hour is assigned a value based on the current hour. Then the IF statement tests whether the hour is earlier than eight in the morning or later than six at night: if so, the protocol executes the proc NightRates. If the condition is false (if the hour is between eight and six), the protocol instead executes the ELSE command, nesting to the proc DayRates:

     ++CHECK.TIME
     Let Hour = $Leftstr($Time,2)
     Let Hour = $Integer(#Hour)
     If #Hour < 8 or >= 18 Then Xeq Proc NIGHT.RATES
     Else Xeq Proc DAY.RATES

Note that the statement converting Hour to type integer will not be needed if Hour is a static variable, defined in a vgroup as type integer -- production applications should typically rely on static, not dynamic, variables. [See 4.]

1.7  Commands Controlling Execution Flow in a Protocol

Like any computer program, a SPIRES protocol benefits from having as clear a structure as possible, so that the flow of execution within it can be easily followed. This section discusses three tools called block constructs that help you create structured programming, and also discusses the important nesting and branching commands, XEQ PROC and JUMP.

Block Constructs for Structured Protocols

The following paired commands, called "block constructs", help control flow of execution in a protocol:

  Block Construct to          Purpose of
  Control Execution Flow      Construct
  -------------------------   --------------------------------------
  BEGINBLOCK                  Declare a block of code to accomplish
  ENDBLOCK                    a single, self-contained task

  REPEAT                      Declare block of code that REPEATs
  UNTIL conditions            execution UNTIL the conditions are met

  WHILE conditions            Declare block of code that continues
  ENDWHILE                    executing WHILE the conditions are true

Each block construct consists of a pair of commands, one command to open the construct and one to close it: within the construct is a "block" of code devoted to accomplishing a single task.

BEGINBLOCK...ENDBLOCK

BEGINBLOCK...ENDBLOCK [See 3.1.1.] is often used to define separate blocks of commands, only one of which is executed, depending on the outcome of a condition-test:

                                           _
  If #Age < 18 Then BeginBlock              |   (This first block gets
    Find Status = Minor                     |--- executed if #Age < 18
    Let MinorCount = #MinorCount + 1        |
    EndBlock                               _|
                                           _
   Else BeginBlock                          |   (This second block gets
    Find Status = Veteran                   |--- executed only if
    Let VetCount = #VetCount + 1            |    #Age >= 18)
    Endblock                               _|

  Type

After executing one of the two blocks above, the protocol automatically goes on to execute the next command in the command stream, which in this case is TYPE.

REPEAT...UNTIL and WHILE...ENDWHILE

REPEAT...UNTIL and WHILE...ENDWHILE [See 3.1.2.] designate blocks that loop (execute over and over again) either WHILE the stated conditions are true or UNTIL the stated conditions become true. Thus, for instance, the entire block below will keep repeating execution until the end-user has finally selected a file, so that the $SELECTED variable is true:

  Set Prompt = 'What subfile would you like to select?'
  Repeat                                                ;SELECTBLOCK
    Ask, Attn = 'Return'
    /Select $Ask
    If $No Then Show Eval 'Please check spelling and try again.'
    Until $Selected = $True                             ;SELECTBLOCK
  Show Eval $Select ' is now selected.'

Incidentally, a useful way to make block constructs easier to follow is to name each block, using the semicolon delimiter as shown above. [See 5.1.]


WHILE...ENDWHILE is similar to REPEAT...UNTIL, but the condition is checked at the beginning of the block. Below, the block is used to add up values stored [See 4.] in the variable array Number. [Variable arrays have not been discussed in this overview chapter, but are a useful way to store related variable values. See Chapter 4 for information.] At each iteration, #Count is incremented by 1 until its value is 6, at which point the block stops executing and the protocol executes the SHOW EVAL command below it:

  Let Count = 1
  Let Total = 0
  While #Count < 6            <--Remember #-sign to avoid infinite loop!
    Let Total = #Total + #Number::#Count
    Let Count = #Count + 1
    EndWhile
  Show Eval 'The total is ' #Total

These examples just give a taste of block structures; later in the document we'll discuss them in more detail, [See 3.1.] and also discuss two commands that let you LEAVE a looping block in its middle, or ITERATE it (start it over from the top) automatically.

XEQ PROC and JUMP

The following commands are also very useful for controlling flow of execution in a protocol:

  Branching Command    Purpose
  -----------------    ---------------------------------------
  XEQ PROC             Cause program to execute the Proc (sub-
                       routine) named with the command

  RETURN               Within a Proc, cause the program to
                       return control to the calling point

  JUMP                 Cause program to branch to label group
                       named with the command

The XEQ PROC command [See 3.2.1.] causes the program to nest to the label statement named with the command, and execute the statements following it as a subroutine; when the protocol encounters a RETURN statement within the subroutine, it "returns" to the calling point in the protocol.

Thus, in the example below, if #Value is greater than 10, the program executes the Proc HIGH.VALUE -- when it reaches the RETURN statement in the HIGH.VALUE Proc, it returns to its calling point, in this case executing the PRINT command:

  ++CHECK.VALUE
  If #Value > 10 Then Xeq Proc HIGH.VALUE
  /Print Copies = #Value
      :
  Return
  ++HIGH.VALUE
  Let Value = 10
  Let HighCount = #HighCount + 1
      :
  Return

Like XEQ PROC, the JUMP command (alias GOTO) [See 3.2.3.] causes a protocol to jump to the label statement named with the command. However, unlike XEQ PROC, JUMP does not "remember" the point in the program from which it was called. Thus, in the example above, you could say "Jump HIGH.VALUE" instead of "Xeq Proc HIGH.VALUE" but you could not count on the RETURN command to "return" you to the calling point in the protocol.

2  Invoking and Executing a Protocol

This chapter discusses the following commands for invoking and executing protocols:

  Command                  Purpose
  ----------------------   ------------------------------------------
  XEQ                      Execute a protocol within your active file.

  SET XEQ                  To set a protocol subfile for execution.
                           (See also SET COMPXEQ.)

  XEQ FROM protocol.name   Execute a protocol from a subfile that
  ..protocol.name          has been set for execution.

  RETURN                   Return to the preceding level in a
  (or CLEAR XEQ)           protocol, e.g., to command level

  CLEAR XEQS               Return immediately to command level

  BREAK XEQ                Cause a temporary break in the execution of
                           a protocol.  (CONTINUE XEQ resumes
                           execution.)

Some other important XEQ commands are also discussed briefly here but in much more detail elsewhere: XEQ PROC, which executes a subroutine nested within a protocol; and XEQ FRAME, which executes an XEQ-type frame within the set format. The chapter also discusses the LOAD PROTOCOL facility for preloading protocols into computer memory [See 2.5.]

2.1  Executing a Protocol in the Active File: The XEQ Command

During your SPIRES session, you can execute a protocol that is in your active file by issuing the following command:

where the AT and USING clauses are optional. Execution in a given line range begins with the label statement indicated in the AT clause.

A line.range is an explicit active file range, contiguous or disjoint, or an associative range. The line.range defines the WYLBUR line numbers of the statements to be executed. Labelname is a label statement [See 3.2.] somewhere in the USING line range; it is the point in the command stream where execution is to begin. If no AT option is given, execution begins with the first line of the USING line range. If, in addition, no USING option is given, the entire content of the active file is executed.

SPIRES uses the command XEQ in place of EXEC or EXECUTE in order to avoid conflicts with WYLBUR's command language.

2.2  Invoking a Protocol from a Subfile: The XEQ FROM and .. Commands

Generally, you store your protocols in (and execute your protocols from) a SPIRES protocol subfile, usually one that you've created yourself, using the PERFORM BUILD PROTOCOLS command. [See 9.]

Schematically, a protocol record stored in a protocol file looks like the example below. (Note the asterisk required in column one of line one, preceding the protocol's name.)

  * Protoname     <--The protocol's name = the key of the record as
                      stored in the protocol subfile.
  (command)
  (command)       <--each line of the record is a single command
     :
     :
     :
  Return

The SET XEQ Command

You may set a protocol subfile for execution by selecting it and issuing the SET XEQ command or by naming the subfile as part of the command, in which case you need not select the file first:

   SET XEQ                     <---if the file is selected
   SET XEQ protocol.subfile

In the second case, replace "protocol.subfile" with the name of the file you wish to set. For example:

  -> select test protocols
  -> set xeq
        OR
  -> set xeq test protocols

Once you have SET XEQ for a particular subfile, it remains set until a SET XEQ is issued for a different subfile selection, SPIRES is exited, or you issue the SET NOXEQ command. Thus, you do not need to have a protocols file currently selected in order to execute a protocol in it.

Compiled Protocols and SET COMPXEQ

Protocols for production applications should virtually always be compiled for efficiency, a process discussed in chapter 10 of this manual. To set for execution a file of compiled protocols, you would use the command SET COMPXEQ in place of (or in addition to) the SET XEQ command. [See 10.3 for a discussion of SET COMPXEQ with full syntax.]

Executing a Protocol

To begin protocol execution, issue either of the following commands:

  -> XEQ FROM protocol.name
  -> ..protocol.name  [parameters list]

The second form is allowed only if protocol.name contains no embedded blanks or special characters other than periods or underscores. (Strictly speaking, you can often get around that restriction by placing the name in single or double quotes, as in ..'two or more words'.) The second form also allows an optional parameters list to be passed to the protocol. The parameter list is passed as a string in the system variable $ASK. [See 6.4.]

For example:

  -> xeq from transcript
  -> ..transcript clear

When a protocol is invoked using either of the above commands, the system first searches the protocol file for which XEQ was last set. If it does not find the named record there, it searches each of the COMPXEQ files in the order in which they were set. [See 10.3.] If the protocol whose name was given in the command cannot be found in any of these files, an error condition is raised.

The user may optionally state the label at which execution is to begin in an AT clause, with a command of the form:

  -> XEQ [ AT label.name ]  FROM protocol.name

Note that there is a similar form of the XEQ PROC command:

which is used when the named protocol is already in the XEQ stack. [See 3.2.1.]

Variables set before the protocol is invoked [See 4.2.1.] are not initialized by the XEQ command; until the protocol explicitly assigns values to them, they retain whatever values were in effect at the time of protocol invocation. However, if you set dynamic variables within your protocol, you may clear them with the $ZAP function or the CLEAR DYNAMIC VARIABLES command. [See 7.2, 8.2.] There are also a number of ways to reinitialize or restore the values of static variables. [See 4.1.1, 4.2.4.]

2.2.1  (Nesting and Chaining Protocols)

The above-mentioned protocol execution commands -- XEQ FROM, .., and XEQ -- can all be issued from within one protocol in order to invoke another, different protocol. That is, the following commands may be embedded in a protocol:

If the execution command is the last statement in the first protocol, then the two protocols are said to be "chained" together, as in the diagram below:

   * Proto1
   (commands)
   ..Proto2 ------------> * Proto2            (Proto1 is "chained"
                          (commands)          to Proto2)
                          Return

The last statement in Proto1 invokes execution of Proto2; when Proto2 encounters the RETURN command it returns to command level.

If the execution command is not the last command in the calling protocol, then the called protocol is said to be "nested" within the calling protocol:

   * Proto1
   (commands)
   ..Proto2 ------------> * Proto2             (Proto2 is "nested"
   (commands) <---\       (commands)           within Proto1)
   (commands)      \_____ Return
   Return

In this case, a .. command within Proto1 invokes execution of Proto2; when Proto2 encounters its RETURN command, it returns not to command level but back to Proto1, which continues executing the next command in its command stream.

The RETURN Command

As the examples above indicate, the RETURN command (alias CLEAR XEQ) always returns execution to the next highest level of nesting (which is simply command level if no nesting has occurred). You can use RETURN in order to return immediate control from a nested protocol to the protocol that called it, as in the example above. RETURN is also useful for returning from nested Procs or subroutines that have been invoked with the XEQ PROC command. [See 3.2.1.]

The CLEAR XEQS Command

The nesting level limit is set to a depth of 100. If execution is occurring in a nested protocol, and you wish to return immediately to command level, you can issue the CLEAR XEQS command. No matter how deeply you are nested, CLEAR XEQS will return you to command level. Note the difference between CLEAR XEQS and CLEAR XEQ (which, like RETURN, only backs up one nested level).

A RETURN command can have virtually any other command (including a RETURN command) appended to it, as in this example:

  Return  Return  Show Eval 'Backing up two levels !!'

This command moves execution back two levels and displays a message at the terminal.

To track nested execution, you can use the SHOW XEQ STACK command [See 3.2.2.]

2.3  Other XEQ Commands

SPIRES offers some other XEQ commands besides the commands discussed so far in this chapter. The commands are treated in more detail elsewhere, but are worth mentioning briefly, for the sake of completeness, in this "XEQ" chapter:

XEQ PROC

The XEQ PROC "label-name" will be covered in detail in a later chapter. XEQ PROC issued within a protocol invokes a subroutine (a proc whose first line is labeled with the '++' prefix) and executes the subroutine, returning after execution to the point from which it was called. For instance the command XEQ PROC GETINFO branches to a subroutine whose first line is ++GETINFO, executes it, and returns to the calling point when it reaches the RETURN statement in the GETINFO Proc.

XEQ PROC is also available, in somewhat different form, as a Uproc within a file definition Userproc or a format. [See 3.2.1 later in this document for more information about XEQ PROC.]

XEQ FRAME

The XEQ FRAME "frame-name" command issued within a protocol invokes a special type of format frame called an XEQ frame, which handles many of the same procedural duties as a protocol, but has easier access to the processing powers of system procs. For instance, the command XEQ FRAME SHOWHELP executes the frame named SHOWHELP in the currently set format, if any. [See the manual "SPIRES Formats" for complete details on XEQ frames.]

XEQ USERPROC

In file definition Userprocs, yet another XEQ command is available as a Uproc: XEQ USERPROC "userproc-name", which invokes another Userproc within the same USERDEFS section of that file definition. [See the manual "SPIRES File Definition" for complete details on Userprocs.]

SHOW XEQ STACK

The SHOW XEQ STACK command lets you trace the hierarchy of nested execution in a protocol. It lists the XEQ commands that have issued (for instance, XEQ FROM and XEQ PROC commands), listing the most recently issued XEQ command first. This command is treated in more detail later in the document. [See 3.2.2.]

2.4  Creating Execution Breaks: The BREAK XEQ and CONTINUE XEQ Commands

In some situations it may be necessary to interrupt execution of a protocol and return end-users temporarily to "command mode". For instance, an execution break might be necessary in a protocol that displayed electronic mail, so end-users could collect a reply in their active file. (Note that since an execution break interrupts your protocol's special features, you'd want to use it with caution.)

More commonly, you may want to break execution of a test or pre-production protocol in order to debug it and test its commands.

The following command issued within a protocol causes an execution break:

To resume execution of the protocol from command mode, the protocol's end-user would type:

(The CONTINUE XEQ command can be abbreviated to its first four letters.)

When a BREAK XEQ command is encountered in protocol execution, the message "-Type CONTINUE XEQ to resume" is sent to the terminal. All system prompts while the break is in effect become "X->" instead of "->" as a reminder that an XEQ is still in effect. (This assumes that you have not SET WYLBUR.) Any protocol, SPIRES or WYLBUR command may be issued in response to the 'X->' prompt.

To exit protocol mode while the break is in effect, and return to command mode, the user could type the CLEAR XEQS command.

Execution Breaks and Debugging

Execution breaks can also be caused by some types of programming errors, and cause the following terminal prompt:

If you respond to this prompt with "BREAK" or "BRE" a normal execution break occurs, as if the "BREAK XEQ" command had been encountered in the protocol command stream. (Other possible responses to the prompt are NO, YES, and OK.)

Within the execution break, you can use debugging features such as SHOW EVAL $LASTXEQ, to determine what command caused the break.

This makes it easier to correct errors that cause execution breaks before you release the protocol for production use. [See 8 and following sections for more on debugging tools.]

Note: To suppress execution breaks within your protocol even after errors, issue the SET NOSTOP command. [See 5.10.] To turn the execution break facility on, issue the SET STOP command. [See 5.10.]

2.5  (*) Preloading Protocols for Efficiency

The following section discusses three optional commands to help a SPIRES application make efficient use of computer memory. (The commands are not needed for Prism protocols, for reasons described further below.)

  Command                 Purpose
  -------------------     ---------------------------------------
  LOAD PROTOCOL ...       Preload a protocol into computer memory
  UNLOAD PROTOCOL ...     Unload a protocol from computer memory
  SHOW LOADED PROTOCOLS   Display the names of preloaded protocols

The LOAD PROTOCOL command preloads the protocol named with the command into computer memory and maintains the protocol there after execution finishes. Preloading is an efficient procedure if the protocols are likely to be called more than once during the end-user's session.

The UNLOAD PROTOCOL command unloads the protocol named with the command.

The syntax for these commands is as follows:

  LOAD PROTOCOL [protocolname]
  UNLOAD PROTOCOL [protocolname]

"Protocolname" represents the name of the protocol you wish to load or unload. For LOAD PROTOCOL, if you leave off "protocolname", the system will ask you which record you wish to load. For UNLOAD PROTOCOL, if you leave off "protocolname", the most recently loaded protocol will be unloaded.

Prism automatically loads and unloads the appropriate protocol records for your application when it calls your file (or calls a particular report or entry form). Thus, Prism protocols automatically benefit from the efficiency of these commands -- you do not need to issue the commands yourself.

How Long the Protocol Remains Preloaded

For a protocol to be preloaded, the subfile containing it must first be set for execution with the SET XEQ or SET COMPXEQ command. But once the protocol is loaded it remains in computer memory until the SPIRES session finishes, or until your code unloads it with the UNLOAD PROTOCOL command. In other words, commands like CLEAR SELECT (to deselect your file) or SET NOXEQ or SET NOCOMPXEQ (to clear the protocol execution file) will not unload the protocol from memory.

For efficient clean-up, you'll probably want to UNLOAD all loaded protocols at the time when people leave your application, in much the same way as you deallocate any global variable-groups ("vgroups") that you previously allocated. (Once again, for Prism protocols, Prism takes care of unloading automatically.)

Example

In a complex SPIRES application, a small protocol might be invoked near the beginning of an application to preload and later to unload the application's main protocols:

  * LOAD.PROTO
      :
  ++LOAD
  Set CompXeq BIG.PROTOS
  Load Protocol MAIN.PROTO1
  Load Protocol MAIN.PROTO2
  Set NoCompXeq
      :
  ++CLEAN.UP
  Unload Protocol MAIN.PROTO1
  Unload Protocol MAIN.PROTO2
  Return

SHOW LOADED PROTOCOLS

To see which protocols are currently loaded, use the SHOW LOADED PROTOCOLS command, which displays the names of loaded protocols beginning with the one most recently loaded:

  -> load protocol proto1
  -> load protocol proto2
  -> show loaded protocols
  - PROTO2                      <--"Last in first out"
  - PROTO1
  -> unload protocol
  -> show loaded protocols
  - PROTO1

When you or a user invokes a protocol by name, using the .. or XEQ FROM command, SPIRES checks the following places (always in this order) for a copy of the protocol to be executed:

Note: it's possible that two or more protocols made available through LOAD PROTOCOL, SET XEQ and/or SET COMPXEQ, might have the same name, in which case SPIRES will use the procedure above in order to determine which protocol should be executed. (If two protocols with the same name are both preloaded with LOAD PROTOCOL, the most recently loaded protocol of the two will be executed.)

3  Controlling Execution Flow in a Protocol

This chapter describes facilities to help control flow of execution in a protocol. [See 1.7 for an introductory overview of these tools.] These facilities help structure code into easy-to-follow subroutines and subprograms. The following topics in particular are covered:


Block Constructs


Nesting and Branching Commands

By the way, most of these facilities are also available as Uprocs within a format or within a file-definition Userproc. (For details see the manuals "SPIRES Formats" and "SPIRES File Definition".) Note also that these facilities to control flow of execution are not available as interactive SPIRES commands -- they can only be issued within a SPIRES component such as a protocol or format.

How These Tools Clarify a Protocol's Structure

The tools discussed in this chapter foster a more "structured" programming, making it easier to construct a protocol from the top down: for instance, you can construct the main body of your protocol as a driver, whose statements provide a sort of high level outline of the program's purpose. The driver can then call on subroutines with XEQ PROC (or XEQ FRAME to call format code), in order to take care of important lower-level tasks.

The driver of a protocol might look almost as simple as this:

  ++HIGH.LEVEL
  Xeq Proc SELECT.FILE
  Xeq Proc SET.FORMAT
  Xeq Proc SEARCH.FILE
     (etc.)
  Return

The subroutines SELECT.FILE, etc., elsewhere in the protocol would take care of the actual business of selecting the file -- the top level of the protocol shows what is being done without having to show how it's done.

Likewise, the block constructs discussed in this chapter are useful devices for organizing related programming statements into complete tasks, with each small-sized task more or less contained within its block. Such devices make it easier to follow a protocol's general design and logic, even weeks or months later, when it needs to be maintained.

3.1  Block Constructs for Structured Programming

A block construct is a special set of paired statements that delineate a self-contained block of commands. Though commands within the block execute in usual fashion, one by one from the top down, you can also think of a block as a complete self-contained entity (almost a little subprogram) which executes either as a whole or not at all:

  If #Num > 2 Then BeginBlock   _
    Let A = 1                    |  If #Num > 2, then the entire
    Let B = 2                    |  block is executed -- otherwise
    Let C = 3                   _|  the entire block is skipped.
    EndBlock

Because block constructs organize commands into larger related units, they are a powerful tool for clarifying the structure of code, and for executing and controlling loops.

There are three varieties of block constructs. BEGINBLOCK and ENDBLOCK [See 3.1.1.] open and close a simple block of commands. WHILE and ENDWHILE [See 3.1.2.] open and close a block that loops WHILE condition(s) stated at the beginning of the block are true. REPEAT and UNTIL [See 3.1.2.] open and close a block of code that loops UNTIL condition(s) stated at the end of the block are true. [See 5.8 for more on conditions.]

A block construct can be diagrammed in three parts:

  (1) BEGINBLOCK        (1) WHILE condition(s)     (1) REPEAT
  (2) commands          (2) commands               (2) commands
  (3) ENDBLOCK          (3) ENDWHILE               (3) UNTIL condition(s)

Statement (1) opens the block of commands (2), and statement (3) closes the block. Note that statement (1) must always be paired with its corresponding statement (3): for instance, every BEGINBLOCK statement must be paired with an ENDBLOCK statement, and vice-versa. Note also that statement (1) is often prefixed by an IF...THEN condition test, as for example, "IF $NO THEN REPEAT". [See 5.8.]

We'll begin by discussing BEGINBLOCK...ENDBLOCK.

3.1.1  BEGINBLOCK...ENDBLOCK

A BEGINBLOCK...ENDBLOCK construct is simply a block of commands, with BEGIN BLOCK beginning the block and ENDBLOCK ending it. (BEGINBLOCK can be abbreviated to BEGIN, or written BEGIN BLOCK, and ENDBLOCK can be abbreviated to ENDB.) A BEGINBLOCK...ENDBLOCK construct almost always follows THEN in an IF...THEN command, as in this example:

  If ~$Selected Then BeginBlock   _
    Select Drinks                  | <--(This can be any block of
    Set Format Display             |    procedural commands)
    Show Eval 'DRINKS selected.'   |
    EndBlock                      _|

BEGINBLOCK...ENDBLOCK constructs are especially useful coded as a pair after a condition test, [See 5.8.] where the outcome of the test results in the execution of only one of the two blocks. For instance, a protocol can test whether a user wants debugging turned on or not, and execute one of two different blocks depending on the answer:

  If #Debugging = $True Then BeginBlock   _
      Set Ftrace                           | (This whole block is
      Set Messages 2                       | executed only if the
      Set Stop                             | condition is true)
      Let TestFlag = 1                     |
      EndBlock                            _|
    Else BeginBlock                       _
      Clear Ftrace                         | (This whole block is
      Set Messages 0                       | executed only if the
      Set NoStop                           | condition is false)
      Let TestFlag = 0                     |
      EndBlock                            _|

Without the block construct, you would either have to use JUMP or XEQ PROC, transferring control to a part of the protocol distant from the IF-test itself, or, if you wanted to keep the code in one place, you would have to execute a cumbersome series of THEN and ELSE commands:

  If #Debugging = $True Then Set Ftrace
    Then Set Messages 2
    Then Set Stop
       :
    Else Clear Ftrace
    Else Set Messages 0
       :

Clearly, the block is easier to read than a series of ELSE commands, and makes the basic structure of the task easier to follow.

Some other interesting features of BEGINBLOCK...ENDBLOCK constructs -- for instance, how they can be chained or nested, or how they save and restore the value of the $ELSE variable -- are covered in an upcoming section. [See 3.1.3.]

3.1.2  Looping Blocks: WHILE...ENDWHILE and REPEAT...UNTIL

The WHILE...ENDWHILE and REPEAT...UNTIL looping block constructs create blocks that execute over and over, either WHILE the stated conditions remain true, or UNTIL the stated conditions become true. (A block construct also terminates execution when it encounters a LEAVE command, a RETURN command, or a JUMP or GOTO command that jumps to a label outside the block.)

WHILE...ENDWHILE

In a WHILE...ENDWHILE construct (where ENDWHILE can be abbreviated to ENDW) the block of commands executes WHILE the conditions stated at the beginning of the block are true. (A LEAVE statement also terminates execution of the block.) Once the conditions cease to be true, the statement following ENDWHILE is executed. If the WHILE conditions are false from the very beginning, the block of commands within the loop never execute at all.

For instance:

  ++ASK.NUMBER
  Set Prompt = 'Please specify a number'
  Ask, Attn = 'Return'
  While $TypeTest($Ask,INT) ~= 'INT'
    Show Eval 'The value must be an integer.'
    Ask, Attn = 'Return'
    EndWhile
  Return

The block above continues executing until the user finally specifies a value that can be converted to integer, at which point the code moves on to whatever command follows the ENDWHILE in the protocol.

One use of WHILE...ENDWHILE is to sum values. The example below initializes a counter variable to zero, then sums up the values contained in five occurrences of the variable array Units. (The block executes five times, and the final value of the Total variable will equal the summed values of Units::0 plus Units::1 plus Units::2 plus Units::3 plus Units::4.)

  Let Total = 0
  Let Counter = 0
  While #Counter < 5
    Let Total = #Total + #Units::#Counter
    Let Counter = #Counter + 1
    EndWhile

You can also use WHILE...ENDWHILE for placing values into arrays:

  Let Counter = 0
  While #Counter < 5
    Let Name::#Counter = $GetUval(Name,#Counter,none,'')
    Let Counter = #Counter + 1
    EndWhile

REPEAT...UNTIL

The REPEAT...UNTIL construct is similar to WHILE...ENDWHILE, except that the condition(s) controlling execution are named at the end of the construct instead of at the beginning. The construct REPEATs execution over and over UNTIL these conditions are true, or until the block encounters a LEAVE statement. (JUMP, GOTO or RETURN would also cause the block to terminate.) For instance, the block below loops until a subfile is selected:

  Repeat
    Show Eval 'First select a subfile.'
    Ask Upper, Prompt = 'Subfile name', Attn = 'Jump Exit'
    /Select $Ask
    If $No Then Show Eval 'Please check spelling and try again.'
    Until $Selected
  Show Eval $Select ' is now selected.'

When to Use REPEAT...UNTIL

Many tasks can be accomplished with either one of the two looping constructs (though WHILE...ENDWHILE is probably more often used than REPEAT...UNTIL). REPEAT...UNTIL comes in most handy when the condition(s) you want to test can only be meaningfully tested after the looping block has executed at least once. This is because a REPEAT...UNTIL construct always executes once, even when its UNTIL conditions happen to be true from the beginning. (That is, the conditions in WHILE...ENDWHILE are checked at the beginning of the block, before it executes; the conditions in REPEAT...UNTIL are checked at the end of the block, after it has executed at least once.)

For instance, in the example below, it only makes sense to test $ASK after the block has executed -- $ASK has a value before the block executes, but it's the value established within the block that is to be tested:

  Let FirstValue = $Int($Ask)
  Set Prompt = 'Specify a second, smaller value'
  Repeat
    Ask, Attn = 'Jump Exit'
    If $Int($Ask) >= #FirstValue Then Show Eval ...
          ... 'Value should be SMALLER than ' #FirstValue
    Until $Int($Ask) < #FirstValue
  Return

(*) The LEAVE and ITERATE Commands

The LEAVE and ITERATE commands help control execution of a looping block more precisely. The LEAVE command always causes the protocol to exit the current looping block and go on to execute whatever command follows the block in the protocol.

For instance, below if an end-user presses ATTN/BREAK (or an equivalent key) in response to the ASK prompt, the block immediately passes control to whatever command follows the block:

  Repeat
    Add
    Ask Upper Prompt = 'Add another record?', Attn = 'Leave'
    Until $Ask = 'NO'

The ITERATE command immediately causes the looping block to execute the closing statement of the block. For WHILE...ENDWHILE, the protocol executes the ENDWHILE statement and immediately bounces backward to recheck the WHILE statement at the beginning of the block. For REPEAT...UNTIL, the protocol immediately executes the UNTIL statement and checks whether the conditions it states are still true. As long as the conditions stated after WHILE (or after UNTIL) are still true, the block is executed again ("iterated"), from the top down.

Neither LEAVE nor ITERATE may be used anywhere except within a looping block.


Looping Block Constructs: Cautions and Considerations

The considerations below may seem obvious, but are probably worth mentioning:

Some restrictions on block constructs are discussed in an upcoming section. [See 3.1.4.]

Note that a looping block construct, like a BEGINBLOCK...ENDBLOCK construct, is able to save and restore the value of the condition test that invoked it. Also, a looping block can be chained to (or nested within) another block. [See 3.1.3 for details on these features.]

3.1.3  Other Features of Block Constructs

This section briefly discusses some additional features of block constructs:

Nested Blocks

Any block construct can be nested within another block. In the diagram below, a WHILE...ENDWHILE construct is nested within a BEGINBLOCK...ENDBLOCK construct.

  If condition1 Then BeginBlock      ; OuterBlock
    commands
    While condition2                 ; InnerBlock
      commands
      EndWhile                       ; InnerBlock
    commands
    EndBlock                         ; OuterBlock

As a courtesy to other programmers who may later need to interpret your code, it's recommended that you "name" nested blocks, using the comment statement or a semicolon delimiter, as in the diagram above. In fact, you may decide to name all your block constructs, not just nested ones.

Although block constructs can be nested, they cannot be interleaved. [See 3.1.4.]

Chained Blocks

Block constructs can also be chained together, as in the example below, which chains a series of condition tests together:

   (1) If ~$Selected Then BeginBlock                     ; Block1
           Let PrevFile = 'NO FILE'
           Select NewBooks
           Let PathOpen = No
           EndBlock                                      ; Block1
   (2)   Else If $Select ~= 'NEWBOOKS' Then BeginBlock   ; Block2
           Let PrevFile = $Select
           Thru NewPath Select NewBooks
           /Set Default Path $PathNum
           Let PathOpen = Yes
           EndBlock                                      ; Block2
   (3)   Else BeginBlock                                 ; Block3
           Let PrevFile = 'NEWBOOKS'
           Let PathOpen = No
           EndBlock                                      ; Block3

If condition (1) is true (and no file is selected), then the first block is executed and blocks (2) and (3), prefixed by ELSE, are completely bypassed. If condition (1) is false, the entire first block is bypassed and condition (2) is tested. If condition (2) turns out to be true, the second block is executed and block (3), because it's prefixed by ELSE, is bypassed. Thus, only if condition (1) and condition (2) both turn out to be false will block (3) be executed.

Block Constructs and $ELSE

An important feature to note is the way a block construct "remembers" and restores the result of the condition test that called it. (That is, a block saves the value of the $ELSE variable [See 6.4.] when it begins execution, and restores this value when the block terminates.) Even if you use IF...THEN within a block to test a local condition, the result of the "larger" condition test (the one that caused the block to be executed in the first place) will be restored when the block is finished.

For instance, the code below tests the value of "Num" and executes one of two different blocks depending on the result of the test. Though there's a "local" condition test of "B" within the first block, that "local" test won't affect whether the second block executes or not:

  If #Num >= 0 Then BeginBlock                  ; Block1
    -  (Block1 executes if #Num >= 0)
    Let A = #Num
    If #B >= #Num Then Let B = #Num
    -  (This IF...THEN won't affect execution of Block2)
    Let C = 3
    EndBlock                                    ; Block1

    Else BeginBlock                             ; Block2
      -  (Block2 only executes if #Num < 0)
      Let Num = 0
      Let A = 0
      EndBlock                                  ; Block2

Note that if you exit a block using JUMP, GOTO or RETURN, $ELSE is not restored to the value it had upon beginning execution of the block.

3.1.4  Some Restrictions

The following restrictions apply to the block construct commands BEGINBLOCK, ENDBLOCK, WHILE, ENDWHILE, REPEAT and UNTIL. Some of them apply to the LEAVE and ITERATE commands as well.

   -> begin block                   <--not valid as interactive command
   -Allowed only in XEQ
    Wdsr, End = 'BeginBlock'        <--BEGINBLOCK is invalid in a clause

    Ask Upper, Attn = 'Leave'       <--LEAVE is allowed in a clause
    Let T = '#X < #Y'
    /While #T                       <--Invalid block construct

    Let T = '#X < #Y'
    While $Test(#T)                 <--This is valid
    If #Status = '' Then EndWhile   <--Invalid command
    If #Status = '' Then Leave      <--Use this instead
   BeginBlock     _                    BeginBlock  _
      :        _   |                      :         |
     Repeat     |  |                     Repeat     | _
      :         |  |  <--valid            :         |  |  <--invalid
     Until ... _|  |                     EndBlock  _|  |
      :            |                      :            |
     EndBlock     _|                     Until        _|

Some other basic restrictions are mentioned in earlier sections -- for instance, every REPEAT statement must be paired by an UNTIL statement, and vice-versa. [See 3.1.]


3.2  Nesting and Branching Within Protocols

The next few sections discuss methods for nesting and branching within a protocol -- i.e., transferring control from one part of a protocol to another:

In addition to nesting within a protocol, you can nest protocols themselves, as described in an earlier section. [See 2.2, 2.2.1.]

Before discussing these commands, we'll begin by describing the label statement in more detail.

Label Statements: The ++ Prefix

You use a label statement in a protocol to name the destination of a branching or nesting command. (You can also use a label statement simply to set off a series of related statements or commands.)

Label statements are of the following form:

The label.name can be up to 16 alphanumeric characters (not counting the "++"). Periods are the only allowable special character in the name -- no embedded blanks are allowed. Label statements cannot be issued as interactive commands.

Some examples of label statements follow:

  ++CHECK.INT  <--A command XEQ PROC CHECK.INT elsewhere in the protocol
                  will cause Proc beginning with this line to execute

  ++900.EXIT   <--A command JUMP 900.EXIT elsewhere in the protocol
                  will jump to this statement

Though labels are optional in SPIRES protocols, they are required in protocols for Prism, and can aid in clarifying the structure of any protocol. For instance, they can often help restructure a large program into a series of smaller and more manageable "subprograms".

3.2.1  Executing Internal Procedures: The XEQ PROC Command

An executing protocol can treat a contiguous series of statements within itself as a closed subroutine or "Proc", whenever the first statement in the subroutine is a ++label.name statement, and the last statement is RETURN.

You invoke such a subroutine internally (i.e., from within the protocol) by using the following command:

  XEQ PROC label.name [IN protocol.name]     <--(or XEQ PROCEDURE...)

where "label.name" is the name of the label that will immediately begin the subroutine. [See 3.2.] For instance, the command XEQ PROC VALIDATE transfers control to a Proc or subroutine labelled ++VALIDATE. When the VALIDATE Proc encounters a RETURN command, control returns to the point in the protocol from which the Proc was called. [See 2.2, 2.2.1 for details on the RETURN command.]

The "IN protocol.name" suffix is described further below.

As mentioned above, the last command in a Proc or subroutine should be RETURN. When the protocol reaches this RETURN statement, it generally returns to (and executes) the NEXT command in the protocol, the command that follows the XEQ PROC command -- the one exception to this is when XEQ PROC occurs as a parameter on the ASK command (e.g., in a NULL clause), in which case the protocol generally returns to the ASK command and executes it again. [See 5.3 for details on the ASK command and its interaction with XEQ PROC.]

Though the analogy should not be pushed too far, in a way a Proc or subroutine can be pictured as a second self-contained protocol sitting nested within its containing protocol.

Example of an XEQ PROC Subroutine

For example, the subroutine labeled CONFIRM below could be invoked repeatedly (by the request XEQ PROC CONFIRM) whenever the program asked a yes-or-no question:

  * SAMPLE.PROTOCOL
      :
  Xeq Proc CONFIRM
      :
  Return
  ++CONFIRM
  Repeat
   /Ask Exact Upper Prompt = '#CurrQuestion' Attn = ''
   Let Response = $PMatch($Ask,Y?ES,N?O)
   If #Response = 0 Then Show Eval 'Please respond Yes or No.'
  Until #Response ~= 0
  Return
      :
      :

(Though not shown, this subroutine would probably also contain some code to handle the situation where the end-user typed HELP or ?.)

XEQ PROC, Branching and Nesting

Most of what XEQ PROC accomplishes can also be accomplished by JUMP or GOTO branching, but the XEQ PROC method of nesting is easier to track, because of the way it returns to a particular place with RETURN. A JUMP statement might jump to any label within the protocol; a RETURN statement within a subroutine can be trusted to return to a particular pre-specified place.

The SHOW XEQ STACK command [See 3.2.2.] or the $XEQSTACK function [See 7.2.] can be very useful for keeping track of where you are in a set of nested subroutines.

Because a Proc is a self-contained unit, it should not include JUMP or GOTO statements that branch outside it, because those statements would defeat the purpose of nesting.

(*) The "IN protocol.name" Suffix to the XEQ PROC Command

In large and complex applications, sometimes the protocol driving the application becomes too large to compile, and must be divided into two protocols, one of which invokes and executes subroutines in the other. If you encounter this situation, for efficiency you should use the "IN protocol.name" suffix on XEQ PROC commands, whenever one of the protocols invokes and executes statements stored in the other protocol. (The "IN protocol.name" suffix is also available on the JUMP or GOTO command.) Both protocols must first have been invoked and brought into computer memory for this technique to work. [To execute a procedure in a protocol that is not currently in the XEQ stack, use the "XEQ AT label FROM protocol" form of the XEQ FROM command. [See 2.2.]]

The best way to explain the "IN protocol.name" suffix is to show a condensed and simplified example:

     * PROTO1                      * PROTO2
        :                             :
(1)  ..PROTO2                         :
        :                             :
     Return                   (2)  Xeq Proc CHECK.NUMBER in PROTO1
(3)  ++CHECK.NUMBER                   :
        :                             :
(4)  Return                        Return

Here the driving protocol code has been split into two protocols, called PROTO1 and PROTO2. When PROTO1 is invoked, one of its first actions (1) is to invoke PROTO2 and bring it into computer memory. Since both protocols are now in computer memory, PROTO2 can include the command (2) XEQ PROC CHECK.NUMBER IN PROTO1, which executes the subroutine CHECK.NUMBER in the other protocol (3). At the RETURN statement (4), control returns to PROTO2.

XEQ PROC is also available in slightly different form as a Uproc in formats and in file definition USERPROCs. See the manuals "SPIRES Formats" and "SPIRES File Definition" for more information.

3.2.2  Tracing Execution: The SHOW XEQ STACK Command

The SHOW XEQ STACK command shows you the hierarchy of nested execution in a protocol, by listing the XEQ commands that have been issued, beginning with the command most recently executed.

For example, consider the following simple protocol, which calls two Procs and then issues a BREAK XEQ command:

Issuing the SHOW XEQ STACK command at the execution break calls up the following display:

If the protocol had been executed from the active file, the calling XEQ command would have been displayed as "XEQ USING" rather than "XEQ FROM TEST".

Once you return to command level, by way of either a RETURN command or a CLEAR XEQ or CLEAR XEQS command, the protocol is no longer nested, so the SHOW XEQ STACK will not return anything. That is, there is no xeq stack at command level.

The SHOW XEQ STACK command may be preceded by the IN ACTIVE prefix to place the display in the active file.

Note that the $XEQSTACK function [See 7.2.] also returns previously issued XEQ commands. For example, $XEQSTACK(0) returns the most recently issued XEQ command, $XEQSTACK(1) returns the XEQ command issued at the previous level (one level higher), and so on. See also the $XEQLVL system variable [See 6.4.] which contains the level to which protocol execution is currently nested.

The maximum number of nesting levels is 128.

3.2.3  The JUMP (or GOTO) Command

To branch within a protocol, you can use the JUMP (or GOTO) command, followed by the label.name of the label to which you wish to branch:

For instance, the statement JUMP MENU causes a protocol to transfer unconditional control to code beginning with the line labelled ++MENU. [See 3.2.]

Code using JUMP can be hard to read and maintain, so SPIRES offers some alternatives that may help create a more clearly structured program. To execute a Proc or subroutine (a self-contained procedure), use the XEQ PROC command rather than JUMP or GOTO. [See 3.2.1.] Unlike code called by JUMP, a subroutine invoked by XEQ PROC "remembers" its calling point and returns there as soon as it encounters a RETURN command. Thus XEQ PROC subroutines are easier to track.

For looping, the block constructs REPEAT...UNTIL and WHILE...ENDWHILE [See 3.1.2.] create structures that are clearer to follow than loops controlled by the JUMP command.

4  User-Defined Variables and Vgroups

This chapter covers "user-defined variables", variables that you as an application developer define and use for temporary storage of values. [Besides user-defined variables the other main category of variables in SPIRES is system-defined variables. [See 6.]] Topics covered in this chapter include the following:

The chapter will also demonstrate how to create and use variable arrays [See 4.3.] and includes a section on dynamic variables. [See 4.4.] Note that, because static variables are more efficient than dynamic variables, static variables and vgroups are the main focus of this chapter.

Advantages of Static Variables

You can create a variable interactively in SPIRES, just by issuing a LET command such as "LET NUMBER = 2". [See 1.2.] The variable ("NUMBER" in the example above) need not exist before you create it in the LET command. (A variable created on the fly like this is called a "dynamic" variable.) Why then go to the trouble of predefining variables in vgroups before using them with a command such as LET?

The main advantage of static variables (variables predefined in a vgroup) is superior efficiency during execution. Static variables are allocated to a reserved space in memory, making them easy for SPIRES to locate during execution. By contrast, since a dynamic variable is defined on the fly, it is much more difficult to locate in memory during execution, and can also cause serious fragmentation of memory.

Static variables also offer greater power and flexibility for defining variable arrays, storing and initializing values, ensuring the data-type of values, and many other important tasks. In general, dynamic variables are best suited for quick interactive use or for testing applications in early stages. (By the way, vgroups themselves may contain variables whose type is "dynamic".) Remember that you can convert dynamic variables into static variables at any time -- in fact, the next section shows how to do this.

Compiled protocols too run more efficiently with static variables. Also, when you compile a protocol, SPIRES will give you warning errors if you use dynamic variables, since statements that use dynamic variables cannot be compiled and thus execute uncompiled. [See 10.2.] In fact, if you use the DECLARE VGROUP or DECLARE GLOBAL VGROUPS commands to define and allocate any variables, then the protocol will not compile at all if SPIRES finds any dynamic variables that are not defined in the local or global vgroups.

4.1  Defining and Compiling a Global Vgroup

When converting the variables your protocol uses into static variables, you choose between two types of vgroups, each with its own construction procedure:

This section describes how to define and compile a global vgroup. The methods for creating a local vgroup are described in the next section. [See 4.1.0.]

To create a global vgroup, you create a record describing the variables to SPIRES, generally following the steps shown below:


Here is the procedure shown in more detail:

Step 1: create the record in your active file.

The record might look something like this:

  VGROUP = gq.jpr.testvars;
  AUTHOR = jeff rensch, 723-2530;
  VARIABLE = integer;                     (See 4.1.1 for a full
    OCC = 1; TYPE = int;                  explanation of each of
  VARIABLE = hexint;                      these elements.)
    OCC = 1; TYPE = hex;
    REDEFINES = integer;
  VARIABLE = question;
    OCC = 1; TYPE = string;
  VARIABLE = answer;
    LEN = 1; OCC = 3; TYPE = char;
    VALUES = A, B, C;

The statements in this record name your vgroup and, for each variable, give it a name (VARIABLE), as well as usually specifying the number of occurrences (OCC), type (TYPE), and initial value(s) (VALUE). [See 4.1.1 for details on all these statements.]


Step 2: Select the VGROUPS subfile in SPIRES and add the record.

  -> spires
  -> select vgroups
  -> add

To update an existing vgroup definition you'd use the familiar TRANSFER and UPDATE commands.


Step 3: Issue the COMPILE command.

  -> compile gq.jpr.testvars
  -Vgroup Definition Compiled
  ->

Or use RECOMPILE instead of COMPILE after modifying an existing vgroup.

You may receive error messages when you compile the vgroup. [See this manual's appendix for a list of error messages.] If errors are found, update your vgroup definition and issue the COMPILE command again.

Using a Global Vgroup

After successfully completing the three steps listed above, you're ready to use your global vgroup in SPIRES. To allocate a global vgroup within a protocol, it is best to use the DECLARE GLOBAL VGROUPS command block:

Another way to allocate a global vgroup, which is not generally recommended within protocols, is to issue the SET VGROUP (alias ALLOCATE) command; to deallocate it when you're through using it, issue the CLEAR VGROUP (alias DEALLOCATE) command. You can do these commands outside of a protocol, or within. However, these commands have no effect on whether SPIRES can find a variable during compilation. If you are compiling the protocol, you will need to use either the DECLARE GLOBAL VGROUP command technique above, or the older technique of allocating the vgroup within a SYS PROTO record. References to variables in a vgroup that has been allocated with SET VGROUP rather than with DECLARE GLOBAL VGROUP or within a SYS PROTO record will not be compiled (though the protocol itself will compile).

But the SET and CLEAR VGROUP technique is very handy in command mode when you are working with a global vgroup, since DECLARE command blocks are not available outside of protocols:

SET VGROUP does have an important use within a protocol: use it there along with the DECLARE GLOBAL VGROUP command if you need the vgroup to stick around in memory after the protocol finishes execution.

Later we'll go into more detail on these commands [See 4.2.1.] but first we'll describe the elements of a vgroup definition in more detail. [See 4.1.1.]

4.1.0  Defining a Local Vgroup

You can define a local vgroup to be used within a protocol two different ways:

The first method, which makes the protocol work the same way whether or not it is compiled, is the preferred one. The second, an older method, is described at the end of Chapter 10. [See 10.8.]

To create a local vgroup with the DECLARE VGROUP command, you add most of the pieces of a vgroup definition record [See 4.1.1.] near the start of your protocol, corralled by a DECLARE VGROUP command and an ENDDECLARE command.

For example, the INTEREST protocol below begins by creating a local vgroup called LOCAL.INTEREST that contains the variables AMOUNT, MONTHS, etc.

A local vgroup declaration begins with the DECLARE VGROUP command:

The "vgroup-name" is a name from 1 to 16 characters. Unlike the name of a global vgroup, which must begin with your account number (since it is the key of a record in the VGROUPS system subfile), this name should not include your account number. (If you include it, it will count as part of your 16 allowed characters.) No blanks are allowed. Using characters other than letters, numerals or periods is not recommended.

The vgroup definition follows, consisting of variable definitions as described in the next section. [See 4.1.1.]

The vgroup declaration ends with the ENDDECLARE command, which has no options:

When you include a declared vgroup in your protocol, SPIRES will not allow your protocol to contain unresolved variable references when the protocol is compiled, nor allow the protocol to access from or store into any variable that has not been declared. In other words, a LET command like LET X = 'ABC' issued outside of a protocol would create a new X variable if it didn't already exist. And the same is true in a protocol that does not contain a DECLARE VGROUP or DECLARE GLOBAL VGROUP command. But if the protocol does have either or both of these commands, then a command such as LET X = 'ABC' will fail if variable X has not been defined within the allocated local and/or global vgroups for the protocol. This is true whether or not the protocol is compiled. [If you try to compile the protocol and it does not contain either of the DECLARE commands described above, then a command like LET X = 'ABC' will generate a warning error from the compiler, because the variable was not found by the compiler. But the protocol will still compile, and SPIRES will create the variable dynamically at execution time. But again, if the protocol does contain either or both of the DECLARE commands, then a command like LET X = 'ABC' will cause the compilation to fail.]

The vgroup will be created and exist for the duration of the protocol's execution; when the protocol goes away, so does the vgroup and its values. The variables of the vgroup are available only within the protocol or outside of it at a BREAK XEQ; they are not available to other protocols. You should not SET VGROUP or ALLOCATE a local vgroup, it is done automatically by the DECLARE VGROUP statement. Lastly, $VINIT of a local vgroup can only be done while the vgroup is available, and you should specify only the vgroup-name, without the ORV. prefix. For example: EVAL $VINIT(LOCAL.INTEREST)

4.1.1  The Elements in a Vgroup Definition

The next paragraphs describe statements in a VGROUPS goal record in detail. Note that most statements described below for a global vgroup definition also apply to definitions for local vgroups that appear in a format definition, in a local vgroup declaration, or in a SYS PROTO record. [See 4.1.0, 10.8.] [See the manual "SPIRES Formats" for more on local vgroups in formats.]

VGROUP = gg.uuu.vgroupname;

"Vgroupname" is the name you choose for the variable group being defined. In combination with your account number, which precedes it, it will serve as the key of the VGROUPS record. You (or your application) will also use this name, prefixed by "ORV." whenever you allocate and deallocate the vgroup. [See 4.2.1.] The total length of the value may not be longer than 23 characters, meaning that "vgroupname" may be no more than 16. No blanks are allowed. Using characters other than letters, numerals or periods is not recommended.

COMMENTS = comments; AUTHOR = name, phone;

Of these two optional statements, COMMENTS contains any comments you wish to include. (It generally tells the purpose of the vgroup.) AUTHOR may be used to hold your name and phone number.

MODDATE = date; DEFDATE = date;

These statements, representing the date the record was added to VGROUPS (DEFDATE) and the date it was last updated (MODDATE), are supplied automatically by SPIRES. You do not need to provide them.

READONLY;

If this statement is coded, it indicates that no values in the vgroup may be changed by the user. Values may only be assigned in the VALUES statement, and may only be changed by changing the VALUES statement and recompiling the vgroup.

VARIABLE = variablename;

This statement provides the name of the variable, which is used to invoke its value. The "variablename" may be from one to sixteen characters long. In general, only letters and numbers should be used in variable names. Typical variable names are:

You may have up to 256 different variables in a vgroup, with each variable array counting as a single variable.

OCC = number.occurrences;

If the variable may have several values, this optional statement defines the number of values. (The default for OCC is 1.) For example, suppose the protocol displays a list of numbered choices and asks the user to specify one or more numbers for the choices desired. The variable representing the choice made could be called CHOICE, and have several occurrences, depending on how many choices the user is allowed to make. Each separate value for the variable may be accessed separately, using subscripting to indicate which specific occurrence (i.e., which "member of the array") is desired. [See 4.3.] The OCC statement specifies whether an array is one-, two- or three-dimensional, depending on the number of OCC values provided.

Note that a variable of type STRING whose length is declared longer than 253 characters may occur only once (see below).

LEN = length;

This optional statement represents the length in bytes of the variable. The LEN value must conform to the restrictions inherent in TYPE.

Default lengths are provided for all types if no LEN statement appears, but for efficiency, you will often benefit from specifying a length. For instance, if a given STRING variable will always be shorter than 80 bytes, it is efficient to code a lower maximum than the default of 80. (If you do not code an explicit length for a STRING variable, VGROUPS adds a "LEN = 80;" statement to your vgroup definition to remind you of the length in effect.)

  Allowed and Default Lengths for Variables

  Variable's Type                           Allowed Length   Default
  ---------------                           --------------   -------
  STRING (Varying length strings)           1-32,765*          80
  INT (Fixed-point binary for integers)     1, 2, 4             4
  REAL (Floating-point binary)              4, 8                4
  PACKED (Packed decimal integer)           1-16                4
  FLAG (On/off)                             1                   1
  CHAR (Fixed length string)                1-256              16
  LINE (Modified INT for line numbers)      4                   4
  HEX (Variable length hex strings)**       1-32,767*           4
  DYNAMIC***                                1-32,765            0
  REF (Reference)                           68                 68

     * The upper limit applies to singly occurring variables; if the
       variable occurs more than once, the upper limit  is  253  for
       string variables and 255 for hex.
    ** When a type HEX variable is compiled it becomes a fixed-length
       value.
   *** DYNAMIC type is useful for large arrays with varying length
       elements.

The total size of ALL variables in a single vgroup cannot be greater than 65,536 bytes. You can define multiple vgroups if you need more variable storage than that.

TYPE = variable.type;

This optional statement specifies the variable type. The choices are as you would guess from the chart above:

The TYPE statement restricts the use of the variable in assignment and computational statements. If no TYPE statement appears for a variable, the variable defaults to type STRING and the variable's length defaults to 80. [See 5.1.2.]

COMMENTS = comments;

You can use this optional statement to document the variable.

REDEFINES = variablename;

This optional statement for a variable (say, variable1) names another variable in the vgroup (variable2). The current value of variable2 will be assigned to variable1 whenever variable1 is used. The value is not converted from the type of variable2 to the type of variable1; instead, the non-converted value of variable2 is "reinterpreted" as a value of "type1". Its effect is similar to the $RETYPE function. [See 7.1.1, 7.2.]

An important restriction of REDEFINES is that a string variable may not redefine a non-string variable. If you needed that capability, you would need to use the $RETYPE command explicitly. Also, variable1 (the redefining variable) may not have a greater length than variable2, the variable being redefined.

A non-string variable may redefine a string variable, though it may only redefine the first occurrence of the string variable. The non-string variable may have multiple occurrences, though. For example, a string variable of LEN = 10 may be redefined by a CHAR variable with 10 occurrences and LEN = 1; each occurrence of the CHAR variable will be a different character of the string variable.

A string variable that redefines another string variable may have multiple occurrences if the LENGTH of each is the same. For example, if string variable X has five occurrences, then string variable Y that redefines X may have from one to five occurrences (from the first to the first five occurrences), if the LENGTH of both variables is the same.

If you explicitly change the value of either variable1 or variable2, the value of the other will also change. Be careful that you do not create a loop of redefined variables, such as variable1 redefines variable2, which redefines variable3, which redefines variable1; such a loop will not compile. You may, however, redefine a redefined variable, through five variables, i.e., var1 redefines var2 redefines var3 redefines var4 redefines var5.

INDEXED-BY = variablename;

This optional statement is used to name the two- or four-byte integer variable or variables whose values represent the subscripts for the variable being described. The INDEXED-BY statement is required for multi-dimensional arrays, and it is recommended for one-dimensional ones. It will be explained fully in the section on arrays. [See 4.3.]

VALUE = value0, value1, ...;

With the optional VALUE statement you can provide initial values for the variable when the vgroup is allocated. These values will always be set each time the vgroup is initialized and may only be changed by changing the VGROUPS goal record and recompiling it.

A variable with only one occurrence may have only one value, and a variable with multiple occurrences may have one value for each allowed occurrence. Values given must be legal for the variable's type or else an error will occur when the vgroup is compiled. (An example of an illegal value would be "string" for an integer variable.) Note that for flag variables, you can give $TRUE or $FALSE as an initial value.

Instead of coding the VALUE statement multiple times when you're defining an array, you can assign a series of values in a single VALUE statement by separating the values with commas:

  VALUE = 0,4,6;

To assign the same value to multiple occurrences in a row, you can put the value in parentheses, preceded by the number of occurrences to be assigned that value. For example,

The first occurrence of the variable will have a null value, but the next 26 will have the value ABC. [See 4.3 for more on variable arrays, including shortcuts for initializing arrays of values.]

DISPLAY-DECIMALS = n;

If the variable is declared to be a PACKED variable, this statement may be included to force the variable value to be stored with the number of decimal places specified by the integer "n". This is similar to the $DECIMAL function. [See 7.] Thus, if variable P is specified to have three decimal places, the command LET P = 1.23456 would assign P the value 1.235. Note that appropriate rounding does take place.

SITE = sitecode;

The singly-occurring SITE statement lets you direct a particular variable to the sites identified by "sitecode", where possible "sitecode" values are CMS, MTS, TSO, and STS (Stanford Time-Sharing system). You can name more than one sitecode in the statement, and can precede the sitecode(s) with the ~ character to indicate that the variable is NOT available at the sites you name:

SITE can be useful when you want to transport an application to sites using a different operating system, and wish to guarantee that the site-dependent version of that variable is invoked only at the appropriate site.

Note that you can declare two variables with the same name within a single vgroup definition, as long as each variable declaration is pointed to a different site. SPIRES will only compile the variable declaration that is directed to the site where the vgroup is being compiled. See also the $SITE variable [See 6.4.] and the SITE statement in format label groups, described in the manual "SPIRES Formats".

4.1.2  (*) Statistics for Compiled Vgroups

You may find it useful to see statistics on the amount of space used by your vgroups in their compiled form. These statistics let you monitor how close your vgroup is to exceeding the maximum size allowed for the internal tables created by SPIRES as is compiles the vgroup.

To obtain statistics, append the STATISTICS option to your COMPILE or RECOMPILE command. For example:

  -> select vgroups
  -> recompile gq.pub.testvars statistics
   VGROUP Statistics
      240 of  4088 Bytes ( 5%) VGROUP Tables
      240 of  4088 Bytes ( 5%) Dictionary
      364 of  4088 Bytes ( 8%) Variable Names
       88 of 32768 Bytes ( 0%) Initial Values
        4 of   300 Bytes ( 1%) CHAR Init Table
   *30432 of 32768 Bytes (92%) Additional Allocation
    31368 of 65384 Bytes (47%) Overall Limit
  -Vgroup definition compiled

The STATISTICS option can be abbreviated to STAT. To direct the statistics to your active file instead of to the terminal, use the IN ACTIVE prefix:

  -? in active recompile gq.pub.testvars stat

An asterisk at the beginning of a line indicates a table that exceeds 90% of the maximum size allowed. (SPIRES would issue this information in a warning message if you omitted the STATISTICS option.) Note that the "Overall Limit" is less than the sum of the tables above it -- you might exceed the overall limit even if the size of the other tables is within the allowed limit.

4.2  Using Global Vgroups and Static Variables

The next few sections describe how to invoke and use static variables after defining and compiling them in a global vgroup. The chapter goes on to discuss variable arrays [See 4.3.] and special uses for dynamic variables. [See 4.4.]

4.2.1  Setting (Allocating) a Global Vgroup

There are four ways to allocate a compiled global vgroup:

This section will describe the first and third methods.

The DECLARE GLOBAL VGROUPS Command Block

To allocate a global vgroup in a protocol, the best method is to place a DECLARE GLOBAL VGROUPS command block near the beginning of the protocol, ahead of any references to variables within the global vgroups. The DECLARE command is allowed only in protocols; it is not available in command mode outside of protocols.

The block consists of three pieces:

The DECLARE GLOBAL VGROUPS and ENDDECLARE commands have no options.

The ALLOCATE statements look like this:

For "global-vgroup-name" it is best to use the form:

where "gg.uuu.vgroup-name" is the key of the vgroup definition stored in the VGROUPS subfile. But if the vgroup is stored under your own account, you can leave off the "ORV.gg.uuu." prefix.

The HIDDEN option tells SPIRES that the vgroup is to be used only within the current protocol; the variables within it will not be visible (or changeable) in other protocols or formats or in command mode.

Up to 16 vgroups can be allocated in a DECLARE GLOBAL VGROUPS command block.

Here's what a DECLARE GLOBAL VGROUPS command block looks like:

When this command block is executed, the GQ.JNK.SPIKE vgroup will be allocated, if it has not already been allocated.

The DECLARE block usually appears near the start of the protocol, in the first few lines. In fact, it could theoretically appear anywhere in the protocol, since the protocol is pre-scanned for vgroup declarations, which are then loaded prior to the protocol execution. That means, for example, that you cannot control vgroup declarations within a protocol with IF...THEN constructs, etc.; if the DECLARE GLOBAL VGROUP or DECLARE VGROUP constructs appear within the protocol, the vgroups will be established.

For example, if your protocol had this:

then the ReplyVars Vgroup would be allocated as soon as the protocol began executing, regardless of the value of #Reply. In other words, there would be no point in doing the IF-test; the vgroup will be created anyway, long before SPIRES gets to the IF-test.

Though it may seem odd for SPIRES to ignore the command-flow aspect of the protocol for these two DECLARE commands, it is important for SPIRES to do so, in order to provide the appropriate variables when an XEQ command jumps into a particular protocol (using XEQ AT... and XEQ PROC... IN...) at a different place than the top of the protocol.

So it is strongly recommended that you put all global vgroup declarations at the start of the protocol.

Note: If your protocol has the DECLARE GLOBAL VGROUPS command block in it, then all variables within the protocol must be defined in one of the allocated vgroups (or in a DECLARE VGROUP command block). An error will occur during execution or when you try to compile the protocol if it contains any non-declared variable references. [See 4.4.]

At the end of the protocol's execution, the global vgroups you have declared in this protocol will automatically be deallocated, unless they have been allocated by another protocol or format that is still running or set, or unless this protocol executed a SET VGROUP command naming the vgroup (see below).

The SET VGROUP and CLEAR VGROUP Commands

Outside of protocols, where the DECLARE GLOBAL VGROUPS method is not available, the SET VGROUP command may be used to allocate a vgroup. SET VGROUP (or ALLOCATE -- they are equivalent) has the form:

For "global-vgroup-name" it is best to use the form:

where "gg.uuu.vgroup-name" is the key of the vgroup definition stored in the VGROUPS subfile. But if the vgroup is stored under your own account, you can leave off the "ORV.gg.uuu." prefix.

The SET VGROUP command is useful within protocols in situations where it is important for the vgroup to remain active after the completion of the protocol. When you allocate a vgroup, the name of the vgroup along with a counter (starting with 1) are put into an internal table. If, say, a format called by the protocol sets the same global vgroup, the counter is bumped up one; when a format or protocol that has allocated the vgroup goes away, the counter is bumped down one. When the counter for a vgroup goes to zero, the vgroup is discarded from memory.

So if you use DECLARE GLOBAL VGROUPS to allocate a vgroup and then later leave the protocol with no intervening "bumps" of the counter, the vgroup will go away too. But if you add a SET VGROUP command for that vgroup within the same protocol, the counter will be bumped up one extra, so that when the protocol ends, the vgroup will remain in memory.

General Information about Global Vgroup Allocation

If a vgroup is already in core when it is allocated from somewhere else, it is not re-initialized; the values currently in it remain in effect. If you need it re-initialized, use the $VGROUPINIT ($VINIT) function.

Clearing a Global Vgroup

When you are through with a global vgroup and no longer need it in the computer's main memory, issue the CLEAR VGROUP command, alias DEALLOCATE, to clear it from memory:

  CLEAR VGROUP full-vgroupname       <--These 2 commands are equivalent
  DEALLOCATE full-vgroupname

where the "full-vgroupname" has the same forms as shown above.

Only these two commands, besides the EXIT command, will deallocate a vgroup allocated by the SET VGROUP or ALLOCATE command.

Note that the CLEAR VGROUP command is not needed for deallocating a local vgroup -- a vgroup defined within a format or SYS PROTO record -- because the deallocation happens automatically when the format is cleared or when the protocol finishes executing. Likewise, even a global vgroup, if it was allocated by the ALLOCATE statement in a format definition or SYS PROTO record, will be automatically deallocated when the format is cleared or the protocol finishes executing. [See 10.5 for more on the distinction between local and global vgroups.]

Seeing the Names of Your Global Vgroups

If you forget the names of your vgroups, you may see them by selecting the VGROUPS subfile and issuing the Global FOR command SHOW KEYS:

You may see the names of only your vgroups in the VGROUPS subfile.

(*) Setting Multiple Vgroups

You can allocate or set more than one vgroup at one time. Be aware, however, that problems may arise if two variables in different vgroups that are allocated simultaneously happen to have the same name. One way to solve this is to give your variables specific prefixes depending on the setting in which they're used. You may also choose to "hide" a vgroup to avoid conflicts, as described above, which limits the scope of its variables to only the protocol (or format) that allocated it.

4.2.2  Assigning Values to Static Variables

The values of static variables may be assigned or changed in various ways. The most common method is to issue the LET command. [See 5.1.1.] Another method is to use the $ASET function to set the values of some or all members of an array to a single value. [See 7.] You may also use the VALUE statement in the vgroup definition to set initial values for variables. [See 4.1.1.] Finally, with the RESTORE STATIC command you can set all the values of all of the variables at once to the values they had at some earlier time which you have stored by issuing the STORE STATIC command. [See 4.2.4.]

The function $VGROUPINIT (also called $VINIT for short) may be used to reset all the variables in the named vgroup to the values they had when the vgroup was first allocated. [See 7.]

4.2.3  Examining the Values of Static Variables

Two commands, the SHOW ALLOCATED command and the SHOW STATIC VARIABLES command, provide you with information on allocated vgroups and static variables. SHOW ALLOCATED displays the names of your currently allocated vgroups. (It does not display the variables themselves.) SHOW STATIC VARIABLES displays the names and values of all static variables currently allocated, as well as the names of the vgroups allocated.

The full syntax of the SHOW STATIC VARIABLES command is as follows:

(Using the IN ACTIVE prefix places the display in your active file.) The three options, all described further below, provide various ways to subset or abbreviate the display of allocated variables.

The "OF vgroupname" option shows only variables in the specified vgroup. If the vgroup belongs to another account, "full-vgroupname" must have the following form:

  ORV.gg.uuu.vgroupname

where "gg.uuu" is the account of the vgroup's owner.

The BRIEF option abbreviates the display of multiple occurrences of a variable when all occurrences have the same value, or no value. (See the example below.)

The "LIKE stem" option limits the display only to variables that begin with the stem that you name.

Sample Displays from SHOW STATIC VARIABLES

SHOW STATIC VARIABLES displays the variables and their current values in ascending sequence by name, with values shown as they would look if they were converted to type STRING. Arrays are shown in order from the first occurrence to the last. [See 4.3.] String-type variables without values (such as QUESTION below) are shown with blank values, while variables of other types that don't have values are shown "zeroed out" (as with INTEGER and INTHEX below). The example below uses the same vgroup defined earlier: [See 4.1.]

The display shown using the BRIEF option would have been identical for this example. However, when the three occurrences of the ANSWER variable have the same value (or no value) their display will be abbreviated with this option:

The line of hyphens signals that all the values in between the first and third occurrences have the same values as the occurrences shown.

You could also subset the display using the LIKE option to "filter" other variables besides ANSWER:

4.2.4  Storing and Restoring Values

The STORE STATIC and RESTORE STATIC commands let you save the contents (i.e., the current variable values) of a static variable group on disk at the end of one SPIRES session and restore the values at the beginning of another session. That is, these commands let you save values across sessions.

The form of the commands is as follows:

  STORE [TEMPORARY] STATIC [ORV.gg.uuu].vgroup ...
        ... TO [ORV.gg.uuu].storage.recname [REPLACE]

  RESTORE [TEMPORARY] STATIC [ORV.gg.uuu].vgroupname ...
        ... FROM [ORV.gg.uuu].storage.recname

"ORV.gg.uuu.vgroup" represents the name of the vgroup record whose values you wish to store. "ORV.gg.uuu.storage.recname" represents the name you wish to use to identify the storage record, stored in the STATIC subfile, as shown further below. These names may be no longer than 27 characters, counting the ORV prefix. In both cases, though the "ORV.gg.uuu" prefix is optional, it's recommended for shared protocols.

The TEMPORARY option tells SPIRES to store the data only for the duration of the session, discarding it when you exit SPIRES. If you don't need the data beyond the current session, this is the best way to save the data: you don't need to invent a unique key name ("storage.recname" in the syntax), nor will you need to remember to ZAP the record at the end of the session (nor will SPIRES be stuck with the record in storage for years if you forget to ZAP it). As a shortcut, you can type TSTAT for TEMPORARY STATIC in both the STORE and RESTORE commands.

The REPLACE option replaces a previous storage record with a new storage record having the same name.

By the way, you may store as many different sets of values, using different "storage.recnames", as you want for a particular vgroup. Note, however, that you may not store values of local vgroups using this method. [See 10.5.]

STORE STATIC and RESTORE STATIC are generally used in close conjunction with the SET VGROUP (ALLOCATE) and CLEAR VGROUP (DEALLOCATE) commands, as shown in the example below. In fact, you must first set a vgroup before you can RESTORE values for it.

Note: If you have SET WYLBUR, be sure to prefix the RESTORE STATIC command with an exclamation point (or slash), so WYLBUR passes the command to SPIRES.

Storing and Restoring Values: An Example

For example, suppose you wish to see all records added to a SPIRES subfile since the last time you checked the file. To accomplish this, you could save a vgroup with a variable containing the current date, later restoring the vgroup contents and using the variable to find records added since the stored date. In fact, you could write a protocol that allocated the vgroup, restored its values, used the restored date to find and display the appropriate records, changed the value to the current date, stored the new value and deallocated the vgroup.

Here is how part of the two sessions described in the example above might look:

In the example, note that $DATE is a SPIRES system variable holding the current date. [See 6.4.]

The RESTORE STATIC command will reset the variables in the vgroup to the values stored. Thus, values provided in a VALUE statement in the vgroup definition [See 4.1.1.] would be changed to the restored values if the RESTORE STATIC command were issued immediately after an SET VGROUP command.

To find out the names of the stored static groups belonging to you, you must use the Global FOR command SHOW KEYS in the STATIC subfile:

The name of each of your stored vgroups, followed by a dollar sign, followed by the name of the vgroup itself is shown. In the example, GQ.JNK.LASTTIME represents "storage.recname", the name of the set of stored vgroup values, and GQ.JPR.RECIPE.EXAMINE is the name of the VGROUP stored in the VGROUPS subfile.

To get rid of a set of stored values for a vgroup, issue the ZAP STATIC command, described in the next section. [See 4.2.5.]

(*) The VALUE statement vs. STORE/RESTORE STATIC

Although both the VALUE statement in the VGROUPS record definition [See 4.1.1.] and the STORE/RESTORE STATIC procedure may be used to store values of vgroups, they each have advantages over the other in different situations.

The VALUE statement is most useful for variables that must always be initiated with the same values. Anytime you wish to change the values that are to be set when the vgroup is allocated, you must update the VGROUPS record and recompile it. Since only you may update your own VGROUPS records, the VALUE statement provides the most security for stored values. It is also convenient for the initial loading of variable values, since you do not have to issue LET statements to give values to the variables the first time you ever use them; the values will always be assigned automatically from the first time the variables are allocated. The VALUE statement may be inconvenient to use with large arrays, however, since all the values are simply listed in a row. It may then be difficult to find a particular occurrence in the long list if you want to change it later.

The STORE/RESTORE STATIC procedure is the method to use in the following situations:

4.2.5  Destroying Vgroups or Stored Static Groups

When you no longer need to store the values of a static vgroup and/or no longer need the vgroup itself, you should use the forms of the ZAP command designed to get rid of them. ZAP VGROUP will eliminate the compiled version of the vgroup and may optionally remove the source record from the VGROUPS subfile, if you so request. The ZAP STATIC command will eliminate one or all of the stored values for a particular vgroup.

To remove the "object code" generated when a vgroup definition is compiled from the VGROUPS subfile, issue the ZAP VGROUP command:

If the SOURCE option is used, the record definition of the vgroup will also be removed from the VGROUPS subfile.

To remove a particular set of stored static variables created by the STORE STATIC command, issue the ZAP STATIC command in this form:

where "storage.record.name" is the same name for the stored variables that you used in the command "RESTORE STATIC vgroupname FROM storage.record.name". [See 4.2.4.]

To remove all sets of stored static variables for a particular vgroup, issue the ZAP STATIC command in this form:

To remove all sets of stored static variables that belong to any of your vgroups, issue the ZAP STATIC command in this form:

The ZAP STATIC command will only remove sets of static records if the logged-on account owns both the storage record and the vgroup. To remove sets with other combinations, you should see your SPIRES consultant.

4.3  Static Variable Arrays

Static variables may exist in one-, two- and three-dimensional arrays in SPIRES. (Dynamic variables can only exist as one-dimensional arrays) [See 4.4.] Arrays are generally used for holding tables that will be referenced by a protocol or format, or for gathering multiply occurring values in an organized fashion for later processing. An array can be considered a table of values in which each value has a common attribute, usually identified by the variable name. For example, an array called CLASSPERIOD might have values representing the times of "periods" in a school day:

Each member of the array is referenced by the variable name and one, two or three integer values, depending on the number of dimensions of the array. In the one-dimensional example above, each individual member is referenced by the name CLASSPERIOD and a single integer value, such as 1, 2, or 3.

One-dimensional arrays are available for both static and dynamic variables. Variables in a one-dimensional array may be referenced by a single subscript. The subscript may be an explicit numeric string, e.g., "12" or it may be a variable that would successfully convert to an integer. Whichever form is used, it is set off from the name of the subscripted variable with a double colon, "::". For example,

In addition, one-dimensional static arrays may take advantage of the more efficient "indexing" capability, which is required for two- and three-dimensional arrays and is described below.

The subscripts indicate the numerical occurrence of the variable. Note however that the occurrences begin counting from zero (0), so that the first occurrence is really numbered 0, the second is 1, and so forth. (It is not required that each occurrence of a variable array have a value, so you could give no value to the "zero-th" occurrence, and begin providing values with the occurrence numbered "1".) The first occurring variable in an array may be specified by either of the following:

Static variable arrays are declared with the OCC statement in the variable definition. [See 4.1.1.] Dynamic variables used in a one-dimensional array, require no special definition but need only be referenced with a subscripted variable in order to exist as an array.

The OCC statement may have one, two or three values, depending on the number of dimensions of the array. "OCC = 3,7;", for example, indicates a two-dimensional array of 21 values. If more than one value of OCC is given, the INDEXED-BY statement must be supplied as well (see below).

To specify a particular occurrence of a two- or three-dimensional array, you must use the indexing feature. You cannot use multiple subscripts. The indexing feature is initiated by the INDEXED-BY statement in the VGROUPS definition record. The INDEXED-BY statement names one, two, or three integer variables (again depending on the number of dimensions of the array) that are also declared in the vgroup definition. The current value of each of these variables will be used as the value for the "subscript" of the array variable when it is referenced using the "INDEX" subscript.

For example, suppose you have an inventory system keeping track of dry goods in a grocery store. Each kind of item is kept on a numbered aisle (dimension 1) on a numbered shelf (dimension 2) at a given location on the shelf (dimension 3). Suppose the vgroups definition contained the following statements:

ITEM is thus a three-dimensional array, indexed by the integer variables AISLE, SHELF and LOCATION. A value stored in ITEM is the name of a particular item.

To use this variable, you need to set the values of the AISLE, SHELF and LOCATION variables first:

When you reference the ITEM array by the index, SPIRES examines the current values of the variables indexing the array. Thus, in the example shown, the ninth item on the second shelf in the third aisle is the product indicated, assuming that the "0" occurrence of the three indexing variables is not used (as described above).

The variables used as indexes must be singly-occurring integer variables of length 2 or 4, as shown in the sample declaration above. The term INDEX must be used as a subscript, and may be abbreviated to I or lengthened to INDEXES, if desired.

Indexing is required for two- and three-dimensional arrays, but is highly recommended for one-dimensional ones because it is more efficient than normal subscripting.

You would probably use a stored static vgroup to load a multi-dimensional array [See 4.2.4.] though it may be done with the VALUE statement. [See 4.1.1.] The first value represents the occurrence of the array where all the indexing variables are 0. The second value represents the occurrence of the array where the first indexing variable is equal to 1, while the others are held at 0. When all occurrences for the first indexing variable have been assigned, the second indexing variable is "bumped" to 1, and the first indexing variable starts over again at 0. The SHOW STATIC VARIABLES command displays the indexes in a translated single-dimension form, displaying the values in the same order in which they would be declared in a VALUE statement.

As an example, here is a sample vgroup definition and the distribution of the values:

After the vgroup is allocated:

Note that the last value was 0 because there were only seven values to be assigned in the VALUE statement. Thus, no value was assigned, and the default value for an integer variable is zero.

Initializing Values in an Array: An Efficient Shortcut

The next few paragraphs discuss an efficient shortcut for initializing arrays in the VALUE statement of a vgroup definition. [See 4.1.1.] In general, you can code a VALUE statement multiple times when you're defining an array, in order to assign values to multiply-occurring variables, or -- more conveniently -- you can assign a series of values in a single VALUE statement by separating the values with commas.

Note that when many of the values will be the same, you do not have to specify each value separately:

  VALUE = 0,0,0,0,0,0,1,2,2,2;

Instead you can specify repeated values by naming the number of values followed by the value itself in parentheses:

  VALUE = 6(0),1,3(2);

Both VALUE statements shown specify the same initialized values, but the second statement causes values to be stored more efficiently, and results in added efficiency in CPU as well.

If you have a single value that contains a comma, the value must be surrounded by apostrophes (quotation marks will not work). Otherwise the value will be split into multiple values at the comma. No single value should exceed 255 characters in length.

4.4  (*) Details on Dynamic Variables

Dynamic variables are created and used in several ways. Most commonly, dynamic variables are defined implicitly when a variable name not known to the system has a value assigned to it. For example, "LET ABC = 123" would create the dynamic variable #ABC if the variable was not defined in any static variable arrays currently allocated or had not previously been defined as a dynamic variable. [See 4.1.]

TYPE and LENGTH are determined by the value each time the variable is assigned a value, i.e., they may change each time a new value is given to the variable. For example, if ABC is not a static variable:

The type and length of the variable change dynamically as the variable is changed; this information is stored with each occurrence. (Note for memory usage purposes that 16 bytes of overhead information are stored for each dynamic variable created.)

Whenever a variable is referenced, the static variables are searched first. Then, if the reference is not resolved there, then the variable is assumed to be a dynamic one if you are in command mode. If the reference is from inside a protocol that does not contain any DECLARE VGROUP or DECLARE GLOBAL VGROUP commands, then the variable is assumed to be a dynamic one. However, if the protocol does have vgroups that were allocated by DECLARE commands, the protocol will give you an error rather than assume you meant to create or reference a dynamic variable. (The point is, all variables used in a protocol should be defined before being referenced, which is good programming practice; otherwise, if you make a typo (like LET SATE = '02/22/94', but you meant DATE), SPIRES will create a new dynamic variable rather than flag the typo as an error.)

Examining Values of Dynamic Variables

You can see the values of all currently created dynamic variables by issuing the command SHOW DYNAMIC VARIABLES (which may be abbreviated as SHOW DYNVAR and preceded by the IN ACTIVE prefix). The values are converted to strings for the display, as they are for the SHOW STATIC VARIABLES command. [See 4.2.3.]

Dynamic Variables in Vgroups

As suggested in the previous sections on static variables, dynamic variables can be declared explicitly in a vgroup definition, e.g.,

Explicit definition is particularly useful in formats and in USERPROCs in file definitions, where all variables used must be declared in a vgroup (or defined in the USERDEFS section, in the case of USERPROCs; see below). Note that LENGTH and OCCURRENCES are not specified since both may vary for dynamic variables.

It might seem puzzling to pre-declare a dynamic variable when the fact is that SPIRES creates them most often when a LET command assigns a value to a variable that is not pre-declared in a static vgroup. However, it is not really the way they are defined that makes variables static or dynamic -- it is the way they are stored in memory. Thus, "pre-declared" dynamic variables are useful in the following situations, which most often arise in large applications:

In all three situations, variables may be defined TYPE = DYNAMIC. Such variables will not take up room in memory until they are used, and then only as much space as they need. Hence, even though they are defined ahead of time in the vgroup definition, they are not allocated space in memory with the rest of the vgroup.

Although a dynamic variable may be declared in a vgroup definition, its values may not be saved and restored by the STORE STATIC and RESTORE STATIC commands. [See 4.2.4.] Moreover, dynamic variables do not disappear when the static vgroup that declared them goes away -- their values may still be retrieved. There are three methods for getting rid of them, short of leaving SPIRES: issue the CLEAR DYNAMIC VARIABLES command (it may be abbreviated to CLEAR DYNVAR), which destroys all dynamic variables currently in existence, or assign the value of the $ZAP function to the variable:

or use the $DYNZAP function:

which, in the example, zaps the first occurrence (occurrence number 0) of the variable #TABLE. [See 7.] The $ZAP function is not available in formats and USERPROCs; the $DYNZAP function must be used instead.

Dynamic Variables in Userprocs

Dynamic variables may also be used to pass values into, out from and between USERPROCs in file definitions. USERPROCs, like formats, may only use variables that are pre-defined (with the exception discussed below). However, USERPROCs may not use variables in global vgroups -- they may only use variables defined in the USERDEFS section of the file definition. Dynamic variables are useful in USERPROCs because unlike other variables defined there, dynamic variables are not cleared when the USERPROC stops executing -- they are available for use outside of the USERPROC, both by other USERPROCs and by commands, protocols and formats. [See the SPIRES manual "File Definition" for more information on USERPROCs.]

Though they are infrequently used, two functions are available for use primarily in formats and USERPROCs to create and retrieve dynamic variables without having to define them in a vgroup. They are $DYNPUT and $DYNGET. [See 7.]

Remember: because dynamic elements cause fragmentation of memory space, they should be used only when necessary and they should be deallocated as soon as you are finished with them.

4.5  Passing Parameters Between System Components

Passing or sharing values among SPIRES protocols and formats can be done in several ways:

Each technique has its advantages and limitations. Using system variables, for instance, is a quick-and-dirty way to pass a value since the variable doesn't need to be defined anywhere; but its name may not relate to the value you are trying to pass, and its value might be changed by other components along the way that use the variable for some other purpose.

The final way listed above, using the $SETPARMS and $GETPARMS functions, is discussed in this section. Basically, you save the values you want to pass with the $SETPARMS function and pick them up elsewhere with the $GETPARMS function, which assigns them to variables of your selection. It is almost as easy as passing values via system variables, but is much more versatile, allowing you to pass values as whatever type (i.e., variable type) they are. Moreover, you don't have to match variable names between the components; you might put the values of variables X, Y and Z into the $SETPARMS function in a protocol, and pull them out with the $GETPARMS function and into variables A, B and C in a format.

This technique is comparable to similar methods of parameter-passing in other languages, such as C and Pascal. Both passing by value and passing by reference (using the $REF function) are available. [See 4.5.1.]

The biggest advantage of this technique is the independence it gives you between the main program and the subroutine. All you need to know is what type of values to pass to the subroutine and in what order you should include them in the $SETPARMS function, and then what type of values and in what order you'll get them back. You don't need to match variable names between the two pieces, nor do you need to work out the complexities of defining global vgroups to share the variable values.

This independence makes this technique very valuable for passing parameters between application components created by different people and different groups. It would not typically be used to pass parameter values to a local subroutine called by an XEQ PROC command, since such subroutines share the same vgroup environment already and therefore already share variables. More likely, you would use them to connect independent modules of an application, or to couple two different applications together -- that is, in situations where actually sharing a variable of a specific name would create an undesirable dependency.

By the way, if you are interested in the other techniques, you'll find information about compiled global vgroups and dynamic variables elsewhere in this manual [See 4.1, 4.4, 10.5.] and also in the Formats and File Definition manuals.

4.5.1  The $SETPARMS and $GETPARMS functions

The $SETPARMS and $GETPARMS functions are used in conjunction to pass parameter values between SPIRES components. They can be coded anywhere in a format or protocol, but generally, you use the $SETPARMS function to store parameter values you want to pass just prior to the program statements that call the next component or subcomponent (such as a format). At the entry point to the subcomponent, you pick up the passed parameter values with $GETPARMS. These functions can be used in many different SPIRES contexts: in protocol invocation, in format and load-format calls, between userprocs and protocols, etc.

Here is the syntax of the $SETPARMS function:

The function may have from 1 to 255 expressions, each of which may be anything that can appear on the right side of a LET command, including fixed values, variables and functions. Hence, a $SETPARMS function that passed three values might look like this:

When executing the function, SPIRES will evaluate each expression, saving each value (and its type, depending on the evaluation of the expression) internally to await the appearance of the $GETPARMS function to retrieve the values. The function returns the number of parameters specified; that number is also saved into the system variable $PARMCNT. ($PARMCNT will be zero if the function fails.) Since that number is saved automatically in the system variable anyway, the returned value is not particularly important; $SETPARMS is commonly used on an EVAL command:

The syntax of the $GETPARMS function is:

The function must currently have exactly the same number of parameters as the previously executed $SETPARMS function; otherwise, an error will result. Each parameter of the $GETPARMS function is the name of a variable, to which the value in the corresponding position of the $SETPARMS function is assigned. Note that you type the name of the variable without the "#" sign, unless for some reason, you want SPIRES to evaluate the variable to get the name of the variable into which the value should be placed.

Like $SETPARMS, $GETPARMS is frequently used with the EVAL command; the value the function returns is the number of parameter values assigned to variables. After $GETPARMS is executed successfully, the value of the $PARMCNT variable is zero.

You can request a specific occurrence of a variable in an array by including the subscript after two colons, e.g., "WithdrawalDay::1". Be sure to put quotation marks around the whole name if you include a subscript. Note that you cannot specify a subscript that is itself a variable in this context, only absolute numeric subscripts.

So, here is the first $SETPARMS function displayed above, followed by a $GETPARMS function that would retrieve the values:

This has the same effect as if three LET statements could have been executed:

(Of course, the presumption is that you could NOT do these LET statements instead, because the variables used in the first component, such as #PayDay, aren't around in, or known to, the second, which is why you're using $SETPARMS and $GETPARMS.)

The variables named may be static or dynamic variables. SPIRES will look for static variables first, of course. If a named variable doesn't exist as a static variable, SPIRES will then create it as a dynamic one.

When it is successful, the $GETPARMS function clears out the values from internal storage within SPIRES; hence a subsequent $GETPARMS function will fail to retrieve any values unless it is preceded by another $SETPARMS function. The function will not be successful if a conversion error occurs, e.g., if you try to put an alphabetic string into an integer variable.

You can use $SETPARMS and $GETPARMS to pass values into a subroutine and back out again. Typically, the $SETPARMS function will appear in the main code just prior to the subroutine call; the $GETPARMS will retrieve the values right at the start of the subroutine code. At the end of the subroutine, to pass values back to the main code, another $SETPARMS function could appear, just prior to the return to the main code. Back in the main code, the next $GETPARMS function would fetch the values being passed back from the subroutine.

However, a better way to return values is to pass variables by reference into the subroutine, so that the subroutine actually changes the variable in the main program, even though it doesn't know the variable's name there. Passing parameters by reference is described in the following section. [See 4.5.2.]

4.5.2  Passing Parameters by Reference

A special capability of parameter passing is the ability to reference and change the value of a variable used in the main program from the subroutine without even knowing the name of the variable. This is done by means of a special type of variable called a "reference variable", whose value is created with the $REF function.

The diagram below shows the general plan for using a referenced variable:

Basically, SPIRES has passed a location of the #Total variable to the subroutine. The #Out variable in the subroutine is tied to the #Total variable so that they in essence share the same value; when the value of one changes, so does the other.

Going through the diagram in detail, we see in the main program that the $SETPARMS function passes a reference-variable value (created by the $REF function), which is picked up in a variable defined as type Reference (#Out) in the subroutine. Any references to that variable then become references to the original, both for reading and for assigning -- though you can restrict reference variables to be read-only. Hence, back in the main program, the value of #Total now has the value assigned to #Out in the subroutine.

So, in that example, the main program sends the subroutine two values and a place to store the value it returns: the referenced variable #Out.

The reference variable must be a static variable that has been defined as type REF. [See 4.1.1.] It can also be defined as an array. In fact, the only way to pass an array for use in the subroutine is to pass it by reference.

To tell a reference variable what variable it will reference, you must use the $REF function, whose syntax is:

where "static-variable-name" is the name of the original variable.

So, if you have the reference variable #Amount (that is, it is defined as type REF), you can assign it as a reference to the original variable #Cost like this:

To make it read-only, you add the ReadOnly parameter to the $REF function:

Making the reference variable read-only prohibits you from changing the original variable's value via changing the reference variable. You can abbreviate READONLY to READ or R.

The $REF function always returns a value of type REF.

5  Protocol Commands and Syntax

This section describes protocol commands that are unfamiliar to SPIRES users who have not used protocols. The commands SET UPPER/UPLOW, SET TRACE/NOTRACE, SET CLEAR/NOCLEAR, SET LIST/NOLIST, SET DELTA and SET LENGTH, all available in protocols, are discussed in other SPIRES documentation.

5.1  Command Statements

Command statements are valid SPIRES commands, protocol commands (see the remainder of the present section), or WYLBUR/ORVYL/MILTEN commands. Generally the protocol command statements discussed here can also be issued in command mode, except those that involve branching or branch points. [See 1.1.] Command statements may be optionally preceded by blanks, and with the continuation character of a reverse slash (\) may continue across several lines within the protocol (this is not allowed in command mode).

Command length limits and command continuation

In theory, SPIRES commands have virtually no length restriction; most can be as long as 32,000 bytes. However, many commands you may want to use in a protocol do have much smaller limits. Any WYLBUR commands you want to include are limited to 133 characters within a protocol, as are ORVYL commands. Moreover, within a SPIRES command, any text string is limited to 256 characters (as in LET X = 'string'; the "string" cannot exceed 256 characters), and occasionally you may find other command components that cannot exceed that length either.

In a protocol, a single line cannot exceed 255 characters, however. To circumvent that restriction, you can use the reverse-slash continuation character at the end of a protocol line. The continuation device is even more useful as a formatting tool, for keeping protocol lines within a set limit for a particular printing device, or for clarifying the structure of long commands, such as PERFORM PRISM SCREEN.

When SPIRES sees a command line with the continuation character at the end, it internally appends the next line of the protocol to that line before executing it. The lines are joined without a break; if you need a space between a word at the end of one line and one at the start of the next line, add one to the end of the first line. Note too that SPIRES will strip blanks from the front of the continuation line; that is helpful when you want to format the protocol nicely so that continuation lines aren't confused with command lines.

Here is an example of a couple lines from a protocol that use the continuation character:

Comments

Almost all SPIRES commands may have comments added to them. (The main exceptions are listed further below.) To append a comment to one of your commands, place a semicolon at the end of the command; the comment begins after the semicolon:

Comments can be very useful when the same protocol will be used over long periods of time or might be maintained by various different developers. They amount to an instant on-the-spot form of documentation.

Comments using the semicolon method may not be added to these four commands:

Note that any semicolon in a command will be considered as a comment delimiter; if a semicolon is to be part of a command parameter, it must be within apostrophes or quotation marks:

Here the record key is "letters;numbers". If no quotation marks were used, SPIRES would stack the key "letters", assuming "numbers" to be a comment.

Comment Statements: The '-' Prefix

The "-" command is an alternate method for supplying comments in protocols when you want the comment to stand alone instead of being appended to a particular command. Both "-" commands and ";" comments are allowed in SPIRES and SPIBILD.

Comment commands are of the form:

where text is any string of characters of length 131 or less. Comment commands (or "dash commands") may be specified in SPIRES and SPIBILD, though the "IN area" prefix is only available in SPIRES.

The comments are displayed online much like * commands whenever ECHO is in effect (i.e., after you issue the SET ECHO command). [See 5.6.] Comments may appear anywhere within a protocol and have no meaning outside it, that is, in command mode. They have no effect on the setting of the $YES and $NO variables. [See 6.4.]

If the comment is a command in a protocol and the "IN area" prefix is included and SET ECHO is in effect, the comment (without the preceding hyphen) will be sent to the specified area.

Also, if SET TLOG is in effect so that tracing messages will be sent to the trace log, comment commands will be sent there too, as well as to any area where other "echo" information would appear. [See 8.4.2.]

The data following the hyphen will be split into multiple lines if necessary. The default line length is $LENGTH or 160, whichever is smaller. The data is broken at the nearest blank before the default line length, if possible.

5.1.1  Variable Value Assignment: The LET Command

Values may be assigned to a variable with a command of the form

where "variablename" refers either to a user-defined variable: either a static variable in an allocated variable group or a dynamic variable. The concept of "expression" includes arithmetical computations (#NUMBER + 1), concatenated strings ('The current time is ' $TIME), and function processing such as $INTEGER(40), as well as simple variables and literals. The equal sign in the syntax of the LET command is optional.

The following are valid LET commands. Note the absence of the '#' or '$' operator on the left side of the assignment statement:

Expressions are always evaluated from left to right, with no operator precedence, but with sub-expressions in parentheses evaluated first.

Note that literals (characters or words, e.g. "+" and ";" above, as opposed to variables) must be enclosed in apostrophes. However, literals in the * prefix should not be enclosed in apostrophes. [See 5.14.]

When a variable name is followed by a literal with no intervening blanks, the literal must usually be enclosed in apostrophes. For example:

In line 6 of the above example, #PART2 is taken as a literal because it is enclosed in apostrophes. It is necessary to enclose a variable name in apostrophes when the name contains the special characters '#' or '$'. [See 11.6, 11.7.]

NOTE 1: There are certain restrictions in the use of mixed types in expressions. [See 5.1.2.]

NOTE 2: The value of a variable, #variablename, is automatically recognized in the LET, EVAL, SHOW EVAL and IF statements, so that the slash prefix to force evaluation is unnecessary (and incorrect) with these commands. For example:

       let linenumber = #lastline            is correct, but
     / let linenumber = #lastline            is incorrect in general.

Other statements, however must force substitution by preceding the command with the prefix '/'. For example:

       / list #linenumber        is correct, but
         list #linenumber        is incorrect.

In general, a LET statement is not preceded by a '/'. However, when you want values to be substituted for variable names and then have the statement evaluated, the '/' is necessary. [See 5.17 for examples of this.]

NOTE 3: The LET command is different from the SET command [See 5.2.] The SET command operates only on system-defined variables [See 6.] and only allows assignments using constants or variables, not expressions using arithmetic or string operators. Only the LET, IF and EVAL commands recognize expressions.

       let linenumber = #firstline - .001       is correct, but
     / set ask =        #firstline - .001       is incorrect;
     / set ask =        #linenumber             is correct.

5.1.1.1  (The Concatenation Operator)

Often when you are assigning values to variables, you want to be able to join two or more values together to create a new value. In other words, you want to concatenate several values. SPIRES recognizes the "||" symbol as meaning "concatenate". For example, if you have two variables, Num1 and Num2,and wish to create a new variable, Num3, made up of the the values of Num1 and Num2 joined together, you could use the following LET statement:

The concatenation operator can be used in any assignment statement, such as LET [See 5.1.1.] or SHOW EVAL. [See 5.14.] But because a concatenated string counts as an expression, it can't be used with the SET command in protocols. [See 5.2.] (Currently, the SET command in formats and file-definition Userprocs allows expressions, but in protocols does not.) There are ways to get around this restriction:

  Let Num3 = #Num1||#Num2
  /Set Ask #Num3

SPIRES will always convert values to type STRING before concatenating them. If the resulting string value exceeds 32K characters, it will be truncated.

5.1.2  Expressions With Mixed Type

Expressions in assignment statements that involve more than a single value such as

can be decomposed into one or more subexpressions of the form

where the binary operators are = , +, -, *, and /.

The allowable TYPE combinations are given below: In the following examples, STRING is type STRING and INTEGER is type INTEGER.

Unless special measures are taken, TYPE combinations other than those mentioned above result in an error. You may circumvent these restrictions with a set of conversion functions, which may be explicitly applied to variables or expressions prior to an operation to ensure a proper type combination. These functions [See 7.1.] will convert a string such as '27' to type INT or LINE, for example, but will set an error flag ('$NO') [See 6.4.] and cause an xeq break if such a conversion is attempted on an alphanumeric string such as '2A'.

Use of binary operators other than '=' in expressions that contain strings will cause an attempt to convert the string value to a numerical variable type. This may cause a 'conversion error'.

For example:

The last command above succeeded because STRING was type REAL. The following command sequence will also succeed:

Even though STRING was dynamically declared as a STRING variable in the first line, the second command succeeds because the value of STRING can be converted to a numerical type.

5.2  The SET Command

The SET command lets you establish or "set" conditions in your SPIRES environment, often by assigning a value to one of the SPIRES system-defined variables. For example, the command SET XEQ TEST PROTOS simultaneously sets the value of the system variable $XEQ and sets the protocol file TEST PROTOS as the XEQ file (the file examined when you invoke a protocol). [Not all system variables can be set. For the system variables useful in protocols, see Chapter 6. For an exhaustive list of system variables, see the short "SPIRES Reference Guide to System Variables".]

The SET command may take one of several different forms:

The first form shown is used to set certain system variables to a value of type STRING, CHAR, INTEGER, or LINE. The second and third forms shown set one or more system variables to values of type FLAG. The fourth form shown sets a feature that may not directly map to a variable name.

In naming a system variable on the left side of the command, you should not prefix it with the '$', any more than you use the '#' on the left side of the LET command. [See 5.1.1 for a comparison of the LET and SET commands.]

Variable Substitution and Special Characters With SET

If you want to set a system variable to the value of another variable, the SET command (unlike LET) must be preceded by a "/":

Furthermore, if #QUESTION contains special characters, such as a hyphen, you should surround the variable name with apostrophes. (If it might have an apostrophe, you should surround it with quotation marks.) Otherwise, the assignment will fail:

The SET Command and Expressions

The SET command in protocols does not allow expressions. (The concept of an "expression" is defined further below.) If you need an expression with SET, you can store the expression in a user-defined variable, using the LET command, then use the variable in a SET command:

     SET ASK $LEFTSTR($ASK,4)     <----This is incorrect
    /SET ASK $LEFTSTR($ASK,4)     <----This is still incorrect

     LET N = $LEFTSTR($ASK,4)
    /SET ASK #N                   <----This is correct

An "expression" is an arithmetical computation, a concatenation of two or more strings, an act of function processing -- in short, everything but a variable or string. In protocols, only the LET, EVAL, and SHOW EVAL commands allow expressions (The IF statement accepts function processing, but not the other two kinds of expressions.) In formats, unlike protocols, the SET command can take expressions. In addition, the SET command in formats automatically performs variable substitution, so that the slash prefix shown in the examples above would be unnecessary -- and incorrect.


5.3  Prompting the End-User for Input: The ASK Command

The ASK command prompts an end-user for input -- often ASK poses a question and requests a response from the user. The user's response to the ASK prompt is placed in the system variable $ASK, where you can test it, manipulate it, or match it against other values. [See 6.4 for more on $ASK, including length limitations.] You can also modify the user's response (or even simulate one) using the SET ASK command. [See 5.4.]

Other options on the ASK command, such as the NULL clause, handle situations where the user fails to respond to the prompted question.

Syntax of the ASK Command

The full syntax of the ASK command is as follows:

The WITHIN TIMELIMIT prefix designates a time limit within which a response should be given. [See 5.20a.]

The UPPER option, if specified on the ASK command, translates the user's response to upper case before storing it in $ASK. UPPER can be useful for later matching with $ASK, since case is significant in the $MATCH and $PMATCH function arguments.

The PROMPT option specifies a string to be used as the prompt. This prompt will be preceded by a colon and followed by a blank unless you also specify the EXACT option, which omits the colon and blank. When you do not specify a PROMPT value here as part of the ASK command, then the current value of the $PROMPT variable will be used as the prompt. (It too will be preceded by a colon (:) and followed by a blank.) By the way, be sure to note that specifying a PROMPT string here will NOT reset the value of the $PROMPT variable -- you would use the SET PROMPT command to accomplish that. [See 5.5, 6.4.]

The NULL clause names the statement or command to be executed when the user enters a null response, i.e., blanks or only carriage return, to the ASK prompt. If no NULL clause is present, the user will be asked the question again. In either case, $ASK will be set to null.

The EXACT option is described above along with PROMPT. The NOTRIM option causes leading blanks to be retained when the user's response is placed in $ASK. Ordinarily these leading blanks are stripped from the response, although any trailing blanks would be retained.

The NOECHO option prevents SPIRES from echoing the response back to the terminal as it is typed. What the user types thus does not show on the screen.

The ATTN clause names the statement or command to be executed when the user enters only an ATTN response to the prompt. If no ATTN clause is present, the ASK command is suspended, and the system behaves as if a BREAK XEQ command has been issued. (In both these situations, $ASK is set to null.) A non-null string followed by ATTN, causes a re-prompt to take place. Note that if you use the ATTN clause, it should be the last clause on the command.

Statements in the PROMPT, NULL and ATTN clauses should be enclosed in apostrophes if they contain embedded blanks.

Here is a sample session demonstrating some of the preceding points:

The ATTN or NULL Clause and XEQ Commands

In the great majority of cases, whenever the statement in a NULL clause or an ATTN clause is an XEQ command, [See 3.2.1.] the XEQ is performed and, upon return, the ASK command is executed again, as the example below suggests:

Note that in certain cases an ASK command is NOT re-executed after an XEQ PROC is completed: this could happen if the PROC issued a command such as "RETURN RETURN" or "CLEAR XEQS", passing over the calling code that included the ASK command. In addition, if ASK is embedded within another command (such as an END clause or a NULL or ATTN clause) it will not be re-executed after an XEQ PROC.

Other Uses and Restrictions for ASK

In addition to being used as a command, ASK may be used as a Uproc in a SPIRES format or in a Userproc in a SPIRES file definition. [See the manuals "SPIRES Formats" and "SPIRES File Definition" for more information.]

There is another form of the ASK command that is used only in full-screen environments -- the ASK AREA command. See Section 4.9 in the "Device Services" manual for information on that command.

ASK should not be used in a Prism application, since Prism itself, not the individual application, handles prompting of the end-user.

5.4  SET ASK

The SET ASK command can be used to simulate a response to an ASK command. In other words, in lieu of storing a user's input in $ASK, you can specify a value to be stored there. [See 6.4.] The length of the string cannot exceed 256 characters.

For example:

You can only specify a string, or a variable that represents a string, on the SET ASK command; you cannot specify an expression. For example,

5.5  SET PROMPT

This command can be used to globally alter the default prompt, ':', sent to the terminal by an ASK command. Any prompt set will always be concatenated to the right of the ':' default.

It is useful to SET PROMPT when several ASK commands will use the same prompt. Also, when the specification of a long prompt in an ASK command with a NULL and and ATTN clause would be more than 234 characters, it is necessary to SET PROMPT before the ASK command, and omit the PROMPT clause from ASK.

If a global PROMPT has been set, the specification of a local prompt in the PROMPT clause of the ASK command will cause the local prompt to override the globally set PROMPT for the single ASK command.

5.6  SET ECHO/NOECHO

Use the SET ECHO and SET NOECHO commands to control "echoing" or terminal display of the commands executing in your uncompiled protocol.

With SET ECHO (the default) in effect, commands are displayed on the terminal as they execute. If SET NOECHO is in effect, the display of commands is suppressed, though informational messages and diagnostics can still be sent to the terminal.

Debugging a protocol is easiest in ECHO mode, while a protocol in actual use would presumably have its echoing suppressed, either through SET NOECHO or, preferably, by being compiled. [See 10 for compiled protocols, which never echo to the terminal.]

To suppress echoing of individual commands, use the ! prefix [See 5.7.]

Note to Prism developers: Prism offers a slightly modified form of the SET ECHO/SET NOECHO facility. For full details, see the "Prism Applications" document. Remember also that echoing is automatically suppressed for compiled protocols, both in SPIRES and in Prism.

5.7  ! Prefix

The ! character or exclamation point, prefixed to a command, has two different potential uses:

The ! command prefix takes the following form:

where "command" is a command, label or comment statement issued within a protocol, or else a command issued online.

The ! Prefix and Passing Commands

For any command, whether issued interactively or in protocol execution, the ! prefix causes the command to be passed directly to SPIRES (or SPIBILD) without being first seen (and perhaps executed or rejected) by some other program.

For example, a user in a dual SPIRES and WYLBUR environment who has SET WYLBUR (so that WYLBUR sees and acts on commands before SPIRES does) would find the ! prefix useful before a SPIRES command such as ALLOCATE, which has a different meaning in WYLBUR. (Although some developers have used the slash prefix / for this purpose, the ! prefix is usually preferable, since it is available in SPIBILD, unlike the / prefix -- also the ! prefix does not perform variable substitution as the / prefix does.) [See 1.3, 5.17.]

The ! Prefix and Echoing

Within uncompiled protocols, the ! prefix is useful to prevent echoing of the SET NOECHO command (and any commands preceding SET NOECHO) in the protocol. SET NOECHO is usually one of the earliest commands issued in a protocol that suppresses echoing, but the ! prefix lets you suppress echoing even of the few commands that precede SET NOECHO in the command stream. Note that, since compiled protocols never echo, their commands would not need the ! prefix for this purpose. [See 10 for compiled protocols.]

The ! prefix will not prevent comment statements, prefixed by a '-', from being echoed in an uncompiled protocol. These statements will always be echoed unless NOECHO has been previously set. Also, unless NOECHO has been set, a label statement of the form !++label.name will echo if executed more than once.

Note that the ! prefix is not allowed before a THEN command or an ELSE command. [See 5.8.1.]

5.7a  : Prefix (The Colon Prefix)

You can write a "one-line protocol" by using a colon before the first command and following each command in the line (except the last) with a pair of separator characters, which are "//" by default.

where "command" is any statement valid within a protocol. Since the "command" can be a label, you can perform looping within this type of protocol (but beware of infinite loops!). Each "command" becomes a line of a protocol that is immediately executed. As with regular protocols, each command is echoed at the terminal as it is issued, unless you precede each command by the "!" prefix or SET NOECHO. [See 5.6, 5.7.] No individual command in the command line can be more than 234 characters long.

You can force variable substitution in a one-line protocol by prefixing the individual command containing the variable with a slash ("/") as usual. [See 1.3, 5.17.] (If you use the default separator between commands, the command will in effect be preceded by three slashes.) You can also prefix the colon by a slash, which would cause variable substitution to take place on the entire command line before it is converted to a protocol and executed.

Here are some examples of one-line protocols:

In the first example, three simple SPIRES commands have been specified. In the second, a series of commands which could, using a large file, take some time has been issued; the user will not have to wait for each command to be executed individually before issuing the next. In the third, notice that variable substitution has been indicated ("/merge #n"); this command chain is designed to merge some data in the active file apparently into each of fifty records that have keys running from "1" to "50".

Here is the terminal display when the second example is issued in an interactive session:

If the user had placed the "!" prefix before each command in the chain, or had SET NOECHO before issuing the protocol, none of the commands would have been echoed.

The separator characters are contained in the system variable $SEP. If you would like to change the value of $SEP to some other characters, use the SET SEPARATOR (SET SEP) command:

where "xx" can be one or two special characters, chosen from the list shown here:

If you specify a single character in the SET SEPARATOR command, that character doubled will be the pair of separator characters; whenever that pair of characters is encountered in a command string beginning with a colon, SPIRES assumes that what follows it is another command. If you specify a pair of characters, that pair will become the separator characters. Thus, if you "SET SEPARATOR @", the value of $SEP becomes "@@"; if you "SET SEPARATOR #=", the value of $SEP is "#=".

5.8  Conditional Statements: The IF...THEN Command

The IF...THEN command lets you test a condition in the IF clause and, if the condition is true, execute the command specified after THEN. The IF clause might consist of a "simple" condition such as the comparison of two values, or might test several "simple" conditions combined by AND or OR:

   IF #A > 0 THEN ...                 Simple condition
   IF #A > 0 AND #B > 1 THEN ...      Compound conditions

After the evaluation of an IF clause, subsequent commands prefixed by THEN are executed if the IF condition as a whole was evaluated as true, and commands prefixed by ELSE are executed if the IF condition was evaluated as false. [See 5.8.1 for more on THEN and ELSE.]

The IF...THEN command in protocols closely parallels the IF...THEN Uproc in SPIRES formats and in file definition USERPROCs.

The syntax of the IF...THEN command is as follows:

  IF condition THEN command

The "command" can be any valid command, and "condition" in its simplest form comes from the list below:

  Simple condition                        Example
  --------------------------------        --------------------
   term                                   If $NO then ...
  ~term                                   If ~#X then ...
   term-1 relational-operator term-2      If #Number > 4 then ...

A "term" can be a user-defined variable, a system variable, a string, or a system function with argument list. The allowed relational operators are =, ~=, >, <, >=, <=. A colon can be used in place of THEN in an IF...THEN command.

The following are sample IF...THEN commands using simple conditions:

  term              IF $PMATCH($ASK,H?ELP) THEN XEQ PROC HELP
 ~term              IF ~$SELECTED THEN JUMP 100.ASK.SELECT
  term-1 = term-2   IF $INTEGER(#A) = 12 THEN XEQ PROC RECHECK.VAL

Remember to consider the data type of terms in an IF clause. For instance, if a variable or a string value will be used in a numerical comparison, it may first need to be converted to a numeric data type, perhaps with a conversion function like $INTEGER -- e.g., IF $INT(#X) > 4 THEN ....

Combining Conditions in an IF...THEN Command

To test more than one "simple" condition within a single IF...THEN command, use the logical operator AND or OR to combine the conditions, as in the examples below: [The maximum number of simple conditions allowed in an IF...THEN command varies, but there is an approximate upper limit of 20 terms per command.]

     IF #FLAG1 AND ~#ERRORFLAG THEN RETURN
     IF #VAL >= 0 OR #NEWVAL <= 100 THEN JUMP DO.SEARCH

(a)  IF #X > 0 AND (#Y < 100 OR #Z = 50) THEN JUMP CANCEL
(b)  IF (#X > 0 AND #Y < 100) OR #Z = 50 THEN JUMP CANCEL

When an IF clause combines three or more simple conditions, as in (a) and (b) above, parentheses may be needed to clarify the relation of the simple conditions. Command (a) above is quite different from command (b), even though their appearance is similar. In (a), #X must be greater than zero for the IF clause to be true. In (b), the IF clause is true whenever #Z equals 50, no matter what #X happens to be.

For situations where command (b) would need the PASS prefix, [See 5.8.2.]

To test the negation of a parenthetical statement, precede the parenthesis with NOT or the ~ character:

  IF NOT (#X > 0 AND #X < 4) THEN SHOW EVAL "Value ...
                                  ... must be between 0 and 4."

Condensing the Syntax of an IF...THEN Command

When an IF clause combines more than one simple condition in the form "term-1 relational-operator term-2," you can sometimes omit the name of "term-1" in the second (and subsequent) conditions, as long as the context makes clear which term you mean:

  IF #VALUE <= 0 OR >= 100 THEN JUMP BAD.RANGE
  IF $LSTR($ASK,1) = "?" OR = "H" THEN JUMP HELP
  IF $INT($ASK) > 4 AND < 10 THEN XEQ PROC SET.DISPLAY

However, you can never omit "term-1" either at the beginning of a parenthesis or just after the close of a parenthesis:

   IF #X > 0 AND (< 10 OR #Y < 100) ...    <---illegal syntax

If omitting a term causes ambiguity, parentheses can help. The three commands below are equivalent and legal, but in the second command it is hard to tell whether #X or #Y is supposed to be less than 10. Parentheses in the third command remove the ambiguity:

   IF #X = 0 AND #Y > 0 AND #Y < 10 THEN...
   IF #X = 0 AND #Y > 0 AND < 10 THEN...
   IF #X = 0 AND (#Y > 0 AND < 10) THEN...

Incidentally, although the second command is legal as shown, it would become illegal if the syntax contained an OR as well as an AND:

   -> if #X = 0 and #Y > 0 or < 10 then * Message
   -Unrecognized: < 10 then * Message

Note that SPIRES will send you a diagnostic message when it finds an error in the syntax of your IF...THEN command.

Caution on Relational Operators in Conditions

Be careful not to omit a relational operator from a condition. For instance, in the condition below, "smith" would be tested as an independent string because of the missing equal sign, meaning that at execution time the condition would always be evaluated as true:

   -> if #lastname = jones or smith then...
   -Warning: String with no relation.

The condition should be rewritten with an equal sign before "smith":

   if #lastname = jones or = smith then...

Or it could be written as follows (this is probably the clearest way to write the statement):

   if #lastname = jones or #lastname = smith then...

The IF...THEN Command With Expressions

Functions (along with their argument list) are allowed within an IF condition, but other kinds of expressions, such as arithmetical computations or string concatenations, are not allowed. You can use a function to get around this limitation:

     IF #LINE     > 20 THEN RETURN             is a legal command,

     IF #LINE + 1 > 20 THEN RETURN             is not.

     IF $INT(#LINE + 1)  > 20 THEN RETURN     is once again legal.

The $EVAL function can also be used to evaluate an expression before testing it. [See 7.2.]

The IF...THEN Command and the Slash Prefix

The IF command does not require a '/' to force the conversion of variables to their string equivalent in the condition portion of the command. Thus the slash prefix is not needed in the IF portion of an IF...THEN command. But a '/' is sometimes required after THEN (or its colon equivalent):

  IF $SELECT = 'RESTAURANT' : * SELECT AGAIN, PLEASE   <--(no slash)

  IF $SELECT = 'RESTAURANT' : / TRANSFER $ASK      <--(slash needed)
  ELSE  SELECT RESTAURANT

5.8.1  The THEN and ELSE Commands

Two important commands, the THEN command and the ELSE command are used to control execution of statements following an IF command. [See 5.8.] (Note that THEN, as well as following IF within an IF... THEN command, can thus begin a new command on a new line.)

A statement in a THEN command is executed only if the most recent IF clause has been evaluated as true; a statement in an ELSE command is executed only if the most recent IF condition has been evaluated as false. (Note that "most recent IF clause" refers to a clause evaluated at that proc or block level; see the final example below.)

The syntax of these commands is:

where "command" is a SPIRES command or a command of some other system, such as WYLBUR or ORVYL. The "command" may be another IF command, though its outcome would affect subsequent THEN and ELSE commands if it were executed.


Here is an example of a protocol using these commands:

The system variables $SELECT and $SELECTED represent the name of the subfile currently selected and the condition of having a subfile selected respectively. [See 6.4.] If the user has no subfile selected, the display will look like this:

If the user had a subfile other than DRINKS selected:

The display when the user already has DRINKS selected can be easily determined.

Note on The $ELSE variable

When an IF clause is evaluated as false, the variables $NO and $ELSE are set to true; the THEN and ELSE commands will later check $ELSE in order to find out whether they should execute or not. The value of $ELSE remains unchanged by THEN or ELSE commands, and is only reset when another IF clause is executed. You should not need to check the value of $ELSE directly, since the THEN and ELSE commands will do that for you.

However, in the days before the THEN and ELSE commands existed, protocol writers had to query the $ELSE variable [See 6.4.] to determine the outcome of a previous IF command; in older code you may still find commands beginning IF $ELSE... or IF ~$ELSE... Be forewarned that querying $ELSE with an IF command in this way will reset the value of $ELSE, so this older code will not work as smoothly as the THEN and ELSE commands do.

Nested IF... THEN, THEN and ELSE Commands

Nested IF, THEN and ELSE commands can be created using the XEQ PROC facility. [See 3.2.1.]

In this case, the value of $ELSE is saved before the PROC is executed and then restored when execution returns. Otherwise, when an incorrect answer was given, the user would be prompted to "Please answer the question", a message meant for users who did not provide any answer at all.

$ELSE is also saved by "BEGINBLOCK" and restored by the matching "ENDBLOCK" (providing you haven't JUMPed into the block from outside). [See 3.1.3.]

Other Restrictions

The ! prefix is not allowed in front of the command that follows THEN or ELSE. [See 5.7.]

5.8.2  Comma Prefix

Many times you want to send a command to the underlying Operating System, without having SPIRES test to see if the command is a SPIRES command. Prefix the command by a comma (,) and SPIRES will send the command to the underlying Operating System. For example:

You could have a slash-prefix before the comma-prefix to cause variable substitutions before sending the result to the underlying Operating System.

You do not need the comma-prefix if the command is obviously not a SPIRES command, but is strictly an underlying system command. Commands passed to the underlying system report success or failure, and this is reflected in the value of $NO. [See 6.24.2.]

The IF...THEN Command and WYLBUR

Because WYLBUR has its own form of the IF command, you sometimes need to take special precautions to make sure the command gets passed to the right system.

If you have SET NOWYLBUR (so that SPIRES sees your commands before WYLBUR does), and you wish to pass an IF...THEN command to WYLBUR, you should precede the command with a comma. For example:

Otherwise SPIRES will assume the command was meant for it.

If you have SET WYLBUR (so that WYLBUR sees your commands before SPIRES does) and your IF...THEN command has a parenthesis immediately after the IF, you may sometimes need to precede the command with the word "PASS" to cause it to bypass WYLBUR and reach SPIRES:

The PASS prefix is most often needed for SPIRES commands within an EXEC file in WYLBUR. It should not be used within a protocol.

Whether you have WYLBUR or NOWYLBUR set, you should take care to skip a space between the word IF and the initial parenthesis of any IF...THEN command directed to SPIRES. The following command would be passed to WYLBUR, not to SPIRES:

5.8.3  Percent Prefix, Unix Emulator

Beside using the comma prefix to send commands to underlying systems, like Unix, you may use a percent prefix. The difference is that the percent prefix won't affect $NO, and therefore you will always get a successful completion indication from a percent prefixed command. For example:

Regardless of how "vi" terminates, this command will always succeed, even when "vi" is not recognized as a system command.

5.9  The SET MESSAGES (SET MES) Command

The SET MESSAGES command in its many different forms lets you control the level and type of system messages sent to the terminal during a SPIRES session. For instance, you can suppress informational messages (such as the result count after a search) with the command SET INFO MESSAGES = 0, or you can cause explanations of serious errors automatically to be sent to the terminal with SET SERIOUS MESSAGES = 4.

The syntax is as follows:

where MES can be used as an abbreviation for MESSAGE. The "type" and "level-number" options are explained below. The features shown within brackets are optional.

After you issue the SET MESSAGES command, system messages are set to the level corresponding to the "level-number" you specify, where the values for level-number are as follows (and the default level-number is 2):

 Level-number:    Message sent:           Example:

   0              No message
   1              Coded message           -133(W)-
   2              Text message            -Zero result
   3              Code and text           -133(W)- Zero result
   4              Code, text and          -133(W)- Zero result
                   explanation            - <explanation>

The "type" option lets you set messages of a certain type, such as warning errors or serious errors. The types are as follows (ALL is the default if you don't specify a "type"):

   Type:    $variable set:    Example Message:           Explanation:

   INFO     INFOX or INFOI    Result: 3 RECORD(S)        Informational
   WARN     WARNX or WARNI    Zero result                Warning
   SER      SERX  or SERI     Serious data error         Serious
   CHR      CHRX  or CHRI     Database charge            Charging
   ALL      (Usually all 4)   (All of the above)         (All of the above)

In addition to setting message levels, the command sets the values of the variables listed above. The $CHRX, $INFOX, $WARNX and $SERX variables are set by protocol commands (the 'X' suffix denoting protocol XEQ mode), while the $CHRI, $INFOI, $WARNI and $SERI variables are set by commands issued interactively (the 'I' suffix denoting 'interactive' mode).

If any of these variables are set in protocol mode (that is, the SET [type] MESSAGE command is issued inside a protocol), the display of protocol-caused messages is affected, but command mode messages are not affected. If any of the variables are set in command mode (that is, in response to a '->'), the display of command-caused messages is altered, but messages in response to protocol-issued commands are not affected.

The database charging messages appear as follows:

Message levels 0 through 3 are available to control these messages, with level 2 the default. The messages are controlled by the following levels:

5.10  SET STOP/NOSTOP

You can use the SET STOP and SET NOSTOP commands to control your protocol's execution in the event of an error condition, whether the error is caused by your end-user's input at the terminal or by a command sequence of your own.

SET STOP is the default; with SET STOP, any command error causes an execution break, and the prompt '-Continue XEQ?' appears on the screen. An invalid command, such as

will cause an execution break.

Using SET NOSTOP, you could allow the failure of a command to be tested by $NO, and take appropriate action if $NO is 'TRUE', indicating the failure of the previous command. This way execution always remains under protocol control, and the need for user-intervention, as in a BREAK XEQ situation, is eliminated.

The following example shows a use of $NO, SET STOP and SET NOSTOP:

If a non-existent goal-key value were input in response to line 2, the TRANSFER command in line 4 would fail. Ordinarily in a protocol such a failure would cause an XEQ break, but by using $NO and SET NOSTOP, execution remains under control of the protocol. STOP should usually be set at the end of such a series of commands, as it is in the last line of the example.

If the TRANSFER command fails, an error diagnostic would be output also; this message and other kinds of messages to the terminal can be suppressed by setting the MESSAGE system variables. [See 5.9.]

5.10a  The SET XSTOP and SET NOXSTOP Commands

The SET XSTOP and SET NOXSTOP commands are used in protocols to handle processing when an error occurs or the user presses ATTN/BREAK. Normally if an error occurs or ATTN is pressed during a protocol, the currently executing command is aborted and execution continues with the next protocol command. If XSTOP is in effect, then an error or an ATTN causes an immediate return from the current level of the protocol. Execution returns through all levels with XSTOP set until either command level or a level with NOXSTOP set is reached. If execution returns to a level with NOXSTOP set, execution continues immediately if NOSTOP is set; if STOP is set, then execution will resume at that level if the user responds affirmatively to the "CONTINUE XEQ?" prompt.

The default setting when a protocol is invoked is SET NOXSTOP. A subroutine (an XEQ PROC) gets its default XSTOP or NOXSTOP setting from its caller. For example, in the protocol below, the subroutine has the XSTOP setting:

If the user presses ATTN while the subroutine is executing, execution would return to the highest level where NOXSTOP is in effect. Since the main code also has an XSTOP setting, the user would be returned to command mode.

The SET XSTOP command allows an XEQ PROC to be interrupted by the user. Because of this, SET XSTOP can be used in subroutines that give instructions or information to the user. If the user already knows the instructions and presses ATTN to stop the listing, command execution could be returned to the main code. For example,

During execution, if the user presses ATTN while the instructions are being displayed at the terminal, execution returns to the main code where NOXSTOP is in effect. (The NOSTOP set at the beginning indicates that no execution prompt will occur if ATTN is pressed.)

If a protocol calls another protocol with the XEQ FROM or "..protocol-name" commands, the called protocol always begins with SET NOXSTOP in effect, no matter what the XSTOP/NOXSTOP setting of the calling routine is. The called protocol could of course SET XSTOP if desired.

5.11  The WDSR Command

The WDSR command reads a line from the active file and places it in the variable $ASK. The full syntax of the command is:

The NULL clause specifies a command to be executed when WDSR encounters a blank line. If this clause is not included, all blank lines are skipped over, and the WDSR command reads the next line with content.

The END clause specifies a command to be executed when the active file is exhausted (i.e., all lines have been read). Although the END clause is marked as optional in the syntax above, omitting it can sometimes cause an execution break.

Here is one way among many to use WDSR:

  ++ReadLoop
  Set Wdsr First
  Let NoMore = $False
  Repeat
     Wdsr End leave
     :
     ; some condition causes you to: Let NoMore = $True
     :
  Until #NoMore = $True
  Return

Setting the Line for WDSR to Read

By default, the initial WDSR command reads the first line of the active file, and each subsequent WDSR command reads the next line. [See 5.13.] Technically, SET WDSR sets the variable $NEXTWDSR, which specifies the minimum line-number of the next line to be read. (Note that SET WDSR sets $NEXTWDSR, not $WDSR as the name might lead you to expect.) Thus the next WDSR command will read the line whose line number is equal to $NEXTWDSR or to the next greater line number. ($NEXTWDSR is set to "0" at the beginning of a session.)

Each time a line is read by the WDSR command, the $YES flag is set and the $NEXTWDSR variable is incremented by .001.

The $WDSR variable always contains the line number of the line most recently read by the WDSR command.

Details on WDSR

If the first line pointed to by SET WDSR is a blank line, then a WDSR command that follows will begin at the first non-blank line after the one pointed to by the SET WDSR command. However, if you specify "NULL = '-'", the initial blank line will be read as any other blank line.

If a WDSR command does not find a line to read and no END clause is specified, $ASK is set to null, but $WDSR and $NEXTWDSR are not affected. This could cause the active file to be read again. Therefore, it is important to have an END-clause.

CAUTION on WDSR

If you mix WDSR with Wylbur or Unix commands, then your active file could possible be read again from the beginning. That's because passing commands to Wylbur or Unix causes the Emulator to detach your active file, and begin reading from the beginning as soon as the last buffer-full of lines is exhausted within SPIRES.

Also, if you are trying to use more than one active file at a time, then the WDSR active file must be the "use" active file (pick 0).

5.12  The WDSW, WDSE, and WDS Commands

The WDSW, WDSE, and WDS commands write lines to the active file. The syntax is:

WDS is equivalent to WDSE. With the WDSW command, the "string" is written to the active file at the line number contained in $NEXTWDSW. If that line number already exists, the contents of that line will be replaced by the string.

The WDSE command will place the string at the END of the active file. END is equal to the last line number rounded up to the next integral multiple of the current value of $DELTA. [See 5.13 for details on how this is calculated.] If there is no active file (and, hence, no last line), the line is written to $DELTA. The variable $WDSW contains the line number most recently written to the active file by a WDSW or WDSE command. [See 6.]

The SET WDSW command lets you assign a value to $NEXTWDSW and control where the next WDSW command will write to. It does not affect the WDSE command.

Like the * command, [See 5.14.] these commands require the slash prefix if the line to be written contains variables to be evaluated. For example,

will write the text to the end of the active file, substituting the logged-on user's account for $ACCOUNT, since '/' is specified.

Note that the IN ACTIVE prefix provides an alternate, somewhat more versatile way of transmitting data to the active file.

5.13  The SET WDSR, SET WDSW, and SET WDST Commands

The SET WDSR and SET WDSW commands allow you to assign values to the $NEXTWDSR and $NEXTWDSW variables, respectively, in order to control reading from and writing into the active file. The SET WDST command lets you set the $WDST variable, which can be used as temporary storage for a line number value.

The syntax common to all these commands is:

The "line #" option lets you specify an explicit line number or a symbolic line number of FIRST, LAST, or END (which may be abbreviated F, L, or E).

For example:

will cause the next WDSR command to read line 3, or the first line following it if line 3 doesn't exist.

Or,

will cause the next WDSW command to write a line at the END of the active file. If there is no active file, $NEXTWDSW is set to $DELTA. The line number is calculated by rounding the line number of the last line up to the next integral multiple of $DELTA. For example:

The "(occurrence) [line range]" option lets you specify a particular occurrence within a line range. The "(occurrence)" parameter must be an integer and specifies the relative line position within the line range, such as the third or fourth line. The line range is specified in the same way that line ranges are specified in WYLBUR. Using the WDSR command will illustrate this:

causes the next WDSR command to read the third line in the active file.

causes the next WDSR command to read the third line within the explicit range 5/L.

causes the next WDSR command to read the third line containing the string 'SPIRES'.

causes the next WDSR command to read the first line containing an occurrence of the string 'SPIRES' beginning in column 56 within the explicit range 5/L.

If no matching string is found in the active file, $NEXTWDSR is set to zero.

NOTE: There is a case where you will encounter an infinite loop when you use the SET WDSR command. As explained, a WDSR command reads the line number contained in $NEXTWDSR. If, after the line range has been exhausted, a WDSR is executed, the statement in the optional END clause is executed; but if $NEXTWDSR has been set by a SET WDSR command that referenced a non-existent line just prior to the WDSR command, the END clause will not be executed, but the first line of the active file will be read.

Given a 2-line active file, the following example illustrates this:

       SET WDSR 1
     ++LOOP
       WDSR  END=RETURN
       JUMP LOOP

This protocol will read the first non-blank line starting with line one and terminate when the third WDSR command is attempted, but this next example will loop infinitely no matter what the size of the active file, since $NEXTWDSR is being set explicitly for each read.

       LET LINE = 1
     ++LOOP
       LET LINE = #LINE + 1
     / SET WDSR (#LINE)
       WDSR  NULL='* Blank line found!'  END=RETURN
       JUMP LOOP

5.14  The "*" Command and the SHOW EVAL Command

This section discusses two different commands for sending a message to users, the "*" command and the SHOW EVAL command. Because the SHOW EVAL command evaluates expressions, it is somewhat more versatile than the "*" command, as will be explained below. [See 5.1.1 for expressions.]

The "*" Command

The * command (or, as it is called, the "star" command) is of the form:

where "*" is an asterisk on the terminal keyboard. The star command provides a method of writing a message to the user's terminal (by default) or to some other device services area if the "IN area" prefix is added. The most commonly used prefix is IN ACTIVE, which directs the message to the user's active file. The CLEAR and CONTINUE options may also be included as part of the prefix.

The "string expression" following the asterisk may have variable names embedded in it, but the variable's value will only be substituted for its name if 1) the entire * command is preceded by the slash prefix ('/') [See 1.3, 5.17.] and 2) the variable name is already known to the system.

The example below illustrates the use of the slash prefix with the "*" command:

Blanks in the string expression are significant, including blanks immediately following the "star".

The message appearing at the terminal will always be prefixed by the '*' to distinguish protocol-generated messages from SPIRES and other system messages. However, when you use the "IN area" prefix, the * does not appear.

The SHOW EVAL Command

The * command can be used to display expressions containing only strings and variables. However, if in addition the expression needs to be evaluated, you should use the SHOW EVAL command instead:

where "expression" is any expression that could be placed in a LET command. [See 5.1.1 for more on expressions.] The expression will be evaluated, converted to a string if necessary and displayed either at the terminal (by default), or to some other area if an "IN area" prefix is added, such as IN ACTIVE.

For example,

SHOW EVAL and the slash prefix

As with the LET, IF and EVAL commands, it is not usually necessary to precede the SHOW EVAL command with a slash (/), unless you want SPIRES to perform variable substitution before it begins any evaluation (which would include variable substitution) of the expression. For example,

The SHOW EVAL Command in SPIBILD and FASTBILD

The SHOW EVAL command is available in SPIBILD and FASTBILD as well as in SPIRES, although some variables (those involving search results or protocol execution, for example) are not available to be evaluated. One likely use of SHOW EVAL in SPIBILD might be to determine which version of the program (i.e., Test or Production) you are currently using:

The command could also be used for on-the-spot computations in SPIBILD or for evaluation of static variables before and after SPIBILD/FASTBILD processing.

The Length of Output From the * and SHOW EVAL Commands

For both the * command and the SHOW EVAL command, if the string result to be output is longer than a certain boundary width, the string is broken into multiple lines at the nearest blank before the width. But a different boundary width is used in different circumstances.

The boundary width used for splitting SHOW EVAL output to the active file or the terminal is $LENGTH or 132, whichever figure is smaller. The width used for a Device Services area such as CRT or FILE is either the width defined for the area or 132, whichever is smaller. [See the manual "SPIRES Device Services" for more information about Device Services areas.]

The boundary width used to split output of the * command (or the dash command) is computed in the same way, except that if the output is being sent directly to the terminal, it will be broken into 160-character chunks; SPIRES will not look for nearby blanks to use for the split, as it does for SHOW EVAL.

5.15  PAUSE

The PAUSE command may be useful when a protocol types more than a few lines of text to the terminal at one time. The PAUSE command will suspend (not break) execution until the user presses the carriage return.

The command is of the form:

At the terminal, the word PAUSE, followed by the text is typed. For example:

If NOECHO has been set or if the PAUSE command has been prefixed by a '!', only a '-' will be sent to the terminal; the word 'PAUSE' and the text message will not appear.

5.16  The EVAL Command

Often, it is desirable to evaluate a variable expression without assignment of the result. The usual reason for executing such a command is to ascertain possible side effects of the evaluation (e.g., conversion errors) without changing the state of any variable. The $NO flag is set ON if the evaluation is unsuccessful.

The following example will test the value of A to see if it is an integer.

A similar evaluation could have been done with the $TYPETEST function: [See 7, 7.2.]

Sometimes it may be necessary to perform a conversion or execute a statement that could possibly fail, and, in the event of a failure, to take action such as printing error messages or re-prompting questions. This would be done with IF, THEN and ELSE statements rather than with the EVAL command. [See 5.8.]

5.17  Variable Substitution

One of the most powerful capabilities of the protocol language is the ability to perform conversion of any variables to their string equivalents prior to parsing and executing the command. Statements for which this is to be done have a '/' as their first character.

For example, consider the sequence:

If the user responded 'JONES', the contents of the system string variable $ASK would be 'JONES'. The evaluation of the string expression FIND AUTHOR $ASK is then FIND AUTHOR JONES. This is the command executed as a consequence of the protocol statement preceded by '/'.

LET, EVAL, and SHOW EVAL commands, and the IF portion of an IF...THEN command, must not be preceded by a '/' except in the unusual circumstances detailed below. All other commands, however, including the SET command, require the '/' if any variables are present to be converted.

(*) The LET Statement and the Slash Prefix

Certain rare uses of the LET statement involving a sort of double substitution may require a '/' prefix. Consider the following examples:

Example 1:

Example 2:

(*) Avoiding Variable Substitution

Sometimes you may want to use the slash prefix on a command without performing variable substitution on every term preceded by a pound sign (#) or dollar sign ($). To guarantee that SPIRES will treat a pound sign or dollar sign prefix as a literal character instead of as a signal for variable substitution, double the prefix character:

  /Print from ##Name #Options     <---(note double "#" before "Name")

In interpreting the command above, SPIRES will substitute the appropriate value for the user variable "#Options" but will know not to substitute a value for "##Name", even if the application developer happens to have a "Name" variable in his or her application. (In this case, the developer probably just wants to print from a data set whose name is "#Name".)

Another example:

  -> Let X = "sample"
  -> /* ##X has the value "#X".
  * #X has the value "sample".
  -> /* The value of $$DATE is $DATE
  * The value of $DATE is 02/24/87

(*) Variables Preceding Other Values

In statements to be evaluated with the slash prefix, where a variable precedes a string without an intervening space, it's strongly recommended that you always place the variable name within quotes to demarcate it from what follows. Consider the example below, where the variable is named DATASET (not DATASET.NEW or DATASET.OLD):

  /Use #'Dataset'.New
  /Copy from #'Dataset'.Old to end

Though in an uncompiled protocol the quotes around the variable name could be omitted, as soon as you compiled the protocol, SPIRES would look for variables named DATASET.NEW and DATASET.OLD and would give you an error message when it failed to find them.

Here is another example, this time using the system variable $ACCOUNT:

  /Use Wyl.$'Account'.Newdata

If the logged-on account were GQ.PUB, this would retrieve the data set WYL.GQ.PUB.NEWDATA.

5.18  Options on the SET TIMER Command: WAIT, PAUSE, and COUNTER

The SET TIMER command, discussed in the manual "SPIRES Searching and Updating", lets you set a timer to issue a message when a specified amount of CPU seconds have been used in a search.

This section discusses three options on SET TIMER that can cause an executing protocol to time its responses in other ways:

  Timer Command            Purpose
  --------------------     ---------------------------------------
  SET TIMER PAUSE          Cause system to pause when a CPU
                           limit has been reached

  SET TIMER COUNTER        Cause system to discontinue a process
                           after a fixed number of timed intervals

  SET TIMER WAIT           Cause system to wait or "sleep" for
                           a specified number of seconds

SET TIMER PAUSE

The SET TIMER PAUSE command, used in conjunction with SET TIMER, helps an end-user to monitor (or interrupt) searching or Global FOR processing that may be using an excessive amount of CPU time. You first set the timer to the appropriate interval, then issue the SET TIMER PAUSE command (which has no options), as in the example below:

  ++SetTimer
  Set Timer 2             <--Sets timer to 2 seconds
  Set Timer Pause
  Return

After the above commands have executed, an end-user whose search or Global FOR processing has taken 2 CPU seconds receives a prompt like the one below:

  -> find subject history
  -Elapsed CPU time: 2 seconds
  -Continue search?

Answering "no" at the "Continue search?" prompt will discontinue the search. Answering "yes" will continue the search, but the end-user will be aware of the expense he or she is incurring.

To turn off this pausing mechanism, you can issue the command SET NOTIMER PAUSE.

SET TIMER COUNTER

The SET TIMER COUNTER command causes a process (a search or Global FOR operation) to terminate after the number of timer intervals specified with the command. Like SET TIMER PAUSE, this command helps control CPU, but gives less control over continuing the processing to the end-user.

The syntax is as follows:

  SET TIMER COUNTER n

where the integer "n" represents the number of timer intervals allowed before an operation terminates. (The timer interval is set by SET TIMER -- e.g., after SET TIMER 5, a single timer interval is five CPU seconds.) COUNTER can be abbreviated down to its first three letters.

  -> set timer 5
  -> set timer counter 2     <--After 10 CPU seconds (5 X 2), an
                                operation will automatically terminate

SET TIMER WAIT

The SET TIMER WAIT command causes an executing protocol to "sleep" for a fixed number of seconds. No prompt is written to the terminal.

The command form is:

where "interval" is a number of seconds from 0.01 to 99999.

Upon execution of this command, the system will pause the specified number of seconds, then execution will continue with the next command.

The user may press the ATTN/BREAK key during this interval, and the system will continue with the next command.

5.19  The WITHIN (WIT) Prefix

If a SET MESSAGES command is issued by a user in command mode, that is, in response to a "-?" prompt, then only messages that result from other commands issued in response to the "-?" prompt are affected. Similarly, if a SET MESSAGES command is issued in protocol mode, only messages arising from commands issued by protocols are affected.

Frequently, a user will want to control command messages from a protocol, or control protocol messages from command mode. For example, users may want to set the level of command messages in their entry commands. This kind of "crossing" can be done by using the WITHIN prefix.

The WITHIN prefix specifies that the command following it is to be executed in the mode specified, with respect to system messages. The mode itself is not changed, but system messages resulting from the command are at the levels in effect for the mode specified. Three modes are presently recognized, INTERACTIVE (for interactive or command mode) and XEQ (for protocol or XEQ mode) and LOG (for data base logging). [See 5.20 for a discussion of WITHIN LOG.]

The syntax of the command is:

WITH and WIT are useful abbreviations for WITHIN; INT is a useful abbreviation for INTERACTIVE.

For example, a user may want to SET SERIOUS MESSAGES TO 4 in his or her entry commands, so that serious error messages that result from interactive commands are explained automatically. The following command would be added to the ENTRY COMMANDS subfile record for the user's account:

The WITHIN prefix can be used with any command except a block construct command. [See 3.1.] Thus, a protocol may set all protocol messages to 0, but still execute selected commands using the interactive message level. For example, a protocol that passes ad hoc user commands to SPIRES may do so via the WITHIN prefix.

5.20  WITHIN LOG Prefix

A command prefix is available to allow users and/or applications programs to direct information to the SPIRES file owner's subfile log. (See "Logging and Charging for Data Base Use in SPIRES," section B.11 in the File Definition manual.)

Any string prefixed by "WITHIN LOG" will be written to the file owner's log -- if the signed-on account's access to the subfile is being logged at any level by the file owner. If no logging is being done for the signed-on account, then the prefix has no effect. After the system has directed the string to the log (whether or not logging is being done), the "WITHIN LOG" prefix is discarded, and the remaining string is passed to the system as a command to be executed.

If the "WITHIN LOG" prefix is itself prefixed by a "/" (the SPIRES evaluation operator), SPIRES examines the command and makes appropriate substitution for any variables it finds, before writing the string to the log.

Strings directed to the log by the "WITHIN LOG" command have a logging code of "CB."

5.20a  The WITH TIMELIMIT Prefix

The WITH TIMELIMIT command prefix, when it precedes a SPIRES command, lets you specify a time limit for a terminal prompt brought about by that command. The prefix can be applied to any command directed to SPIRES, though not to commands directed to other systems such as WYLBUR.

The full syntax is as follows:

Here "n" represents the number of seconds in the time limit. 1 second is the minimum value permited for a time limit. (Actually, you could also specify a value of "0", but that would be equivalent to leaving the prefix off altogether.) 6000 is the maximum value; you can enter a higher number, but SPIRES will use 6000.

The WITH TIMELIMIT prefix works together with the $TIMELIMIT variable [See 6.4.] in the following way. WITH TIMELIMIT "n" in front of a command sets $TIMELIMIT to a value corresponding to the value of "n", and simultaneously sets a timer of "n"-second duration for any terminal prompts issued with the prefixed command. If your user responds to the prompt within the time limit specified, $TIMELIMIT is reset to zero when the command is completed. But if the user fails to respond in time, the timer "fires", giving $TIMELIMIT a negative value. Since $TIMELIMIT remains negative until another WITH TIMELIMIT prefix occurs, or until the terminal is prompted again, your protocol can test the variable for a negative value and take whatever action seems most appropriate.

During the execution of the prefixed command (i.e., in the period of time before either the user responds or the timer fires), $TIMELIMIT remains set to "n". Thus you could test its value from a format frame to see whether a timer is in effect or not. (If $TIMELIMIT is 0, then no timer is in effect.)

Some possible uses of the WITH TIMELIMIT prefix follow:

[The prefix has many possible uses in full-screen programming as well. See the manual "SPIRES Device Services" for more information.]

The WITH TIMELIMIT Prefix with the ASK Command

Within a protocol, WITH TIMELIMIT can be used as prefix to an ASK command that has a NULL clause. The action that you specify in the NULL clause would be taken if the user should press RETURN, but would also be taken if the time limit expired and the timer fired. Here is one example:

In this case, if the user responds to the prompt with a carriage return, or if the timer fires before the user responds at all, the protocol will jump to ++RECORD, where you can test which of the two events occurred. At ++RECORD, if $TIMELIMIT is less than zero, the timer must have fired; otherwise the user's response was a simple carriage return.

Note that in full-screen programming the WITH TIMELIMIT prefix can be used in a somewhat similar manner together with commands such as ASK AREA. [Again see "SPIRES Device Services" for more information.]

5.21  (*) Commands for Monitoring Use of Memory

The next two sections describe commands that help large applications monitor their use of computer memory. These commands are only likely to be useful to very large applications.

5.21.1  (*) The SHOW FREE CORE Command

The SHOW FREE CORE command (alias SHOW FREECORE) shows you how many free memory segments are available in computer memory, as well as the size of each segment, and where in memory they reside. The command is primarily designed for large applications that need to monitor closely the amount of computer memory available.

The syntax is as follows:

  [IN ACTIVE] SHOW FREE CORE

The IN ACTIVE prefix puts the display in your active file, though this display will not include information on the portion of core (usually pretty small) that the IN ACTIVE command itself uses.

Here is an example of the command:

  -> show free core
      List of Free Memory Segments   |  Used Core
    Start     End   Size (Hex / Dec) |  Size (Dec)
   0749F30  0749F38       08       8 |        4288
   0749E90  0749E98       08       8 |         152
   0749208  0749978     0770    1904 |        1304
   0744148  0744168      020      32 |       20640
   07440D0  0744138      068     104 |          16
   07075F0  0744088   03CA98  248472 |          72

The first two columns indicate the starting and ending addresses of each free memory segment. The third column shows the size of the segment in hexadecimal bytes and the fourth column shows the number of bytes as a decimal value. The fifth column shows the amount of core that has been used. The ending address given for each free segment is the starting address of the used core space given on that same line.

In the example above, the 32 bytes of free space starting at 0744148 (line four) are followed by 20640 bytes of used space beginning at 0744168 and ending at the start of the next free space (0749208 shown on the preceding line).

This command is primarily useful to see if computer memory has become fragmented such that there is not a piece of memory large enough to work with. If you continually get "Core exhausted" messages or errors such as S198 errors during execution of your application, the chances are good that "core" (computer memory) has become fragmented. This usually only occurs in applications utilizing a large number of pieces, such as formats, vgroups, dynamic variables, and paths. To solve core-fragmentation problems, it's important to establish all these pieces in the proper sequence in order to use memory space efficiently.

5.21.2  (*) The SHOW SUBFILE MAP Command

The SHOW SUBFILE MAP command, like SHOW FREE CORE [See 5.21.1.] helps extremely large applications monitor their use of computer memory.

The syntax is as follows:

  [IN area] SHOW SUBFILE MAP

where the optional "IN area" prefix places the display into the specified area. (A likely "area" would be ACTIVE for the active file.)

SHOW SUBFILE MAP displays and labels each piece of your application that is loaded into memory at the time you issue the command. (Important exception: dynamic variables currently are not included in the "map".) If your application encounters errors due to lack of sufficient computer memory, such as S198 errors or "core exhausted" conditions, you can use this "map" to determine whether pieces are loaded that should be released in order to free memory.

A very simple example appears below, though the large applications using this command will produce much longer displays:

  -> select restaurants
  -> set format update
  -> thru mypath set format restrnts display
  -> show subfile map
     06/10/1987   Subfile Memory Map         Start     End     Size

    Primary file: GQ.DOC.RESTAURANT
    Subfile: RESTAURANTS                      06146D0  0617480   11696
    Format: UPDATE                            06CD380  06CD740     960
            Format control                    06CD740  06CD8F0     432

    Path  2
    Format: RESTRNTS DISPLAY                  06CCBA0  06CCF60     960
            Format control                    06CCF60  06CD380    1056

      Set XEQ RCT table                       0E3B128  0E3B170      72

    $ASK space:                               0E3DC60  0E3DD68     264
    $PARM space:                              0E3DD68  0E3DE70     264
    $PROMPT space:                            0E3DE70  0E3DF78     264
    Temporary file data:                      0E3CA90  0E3CC20     400

    Device Services:
       DAT Chain Region:                      0E3C4C0  0E3C8B0    1008
    System area: NULL                         0E3DF78  0E3DFB8      64
    System area: TER                          0E3C950  0E3C990      64
       Buffer:                                0E3C470  0E3C4C0      80

The map shows the internal hexadecimal address of the "Start" and "End" of each piece of the application, as well as its "Size" in bytes. A developer running into memory problems might note from the map that a format is still set even though it's no longer needed -- thus the map might help pinpoint pieces of code that should be released from memory.

Note that in some cases an application's piece -- e.g, a format invoked from more than one path -- might appear twice in the map, but it will have the same "Start" and "End" addresses, indicating that SPIRES is sharing a single copy of that code for efficiency.

5.22  The SET WIDTH Command

The SET WIDTH command allows you to set a value indicating the width of your terminal. The value is stored in the $WIDTH variable. If you have WYLBUR mode set, you must precede the command with a slash (i.e., /SET WIDTH 65). It can be up to 235.

By issuing a SET WIDTH command during a session, you can dynamically alter the dimensions of an area if you have defined that area to use the default width. The default is 80 at the beginning of a SPIRES session. This value is independent of the MILTEN width setting.

5.23  The WRITE FILE LOG Command

Logging of data base use is controlled through statements in the file definition. Certain commands are written to the file log, which is kept in the deferred queue until the file is processed. [EXPLAIN LOG FILE for more information about subfile logging.]

While a subfile is selected, the log is created in core, and periodically written to the deferred queue. When the user "unselects" the subfile, the remainder of the log is written from core to the deferred queue. If a session is interrupted by a session break or a system failure, any logging data that is in core at the time will be lost. The WRITE FILE LOG allows you to force the logging data to be moved to the deferred queue. This is useful in applications that are not logging all commands or responses to prompts, but are only occasionally writing log information via the WITHIN LOG prefix. [See 5.20.] For example, if you are only counting certain transactions, and issue a WITHIN LOG ADD command, you might want to follow it with a WRITE FILE LOG command to insure that the logged transaction is written to the deferred queue.

5.24  The CLEAR MESSAGE NUMBER Command

The CLEAR MESSAGE NUMBER command sets the $SNUM, $PSNUM, $ENUM and $MNUM variables back to 0. These variables are assigned values when an error is encountered, so you might want to clear them once you have dealt with an error condition.

5.25  The SET SUBLATCH (SET SUBLAT) Command

The SET SUBLATCH command sets the value of the system variable $SUBLATCH. [See 7.3.29d.] The syntax is:

where "string" can be any string value up to 32,767 characters long.

This command can be issued only once for each selected subfile. To reset the $SUBLATCH variable, you must reselect the subfile. You can issue the SET SUBLATCH command through a subfile path, but the value of $SUBLATCH is only available if that path is the default path (i.e., if you have issued the SET PATH command).

6  System-Defined Variables

System variables are those variables that are predefined and maintained by SPIRES. The values of system variables may change as a result of the commands you issue during a session. System variables are used primarily to provide information about your session, what subfile you have selected, if you have a search result, or what command has just been issued. They also hold some "constants" such as date, time, or terminal type. You can also set some system variables to control how protocols execute or how SPIRES is to process certain commands.

The names of system variables all being with a "$". Some, but not all, system variables can have their values set or changed by a direct command, such as the SET command. You can find the current value of most system variables by issuing the SHOW EVAL command, e.g., SHOW EVAL $SELECTED. Some variables are only allowed in formats or in user-defined processing rules in file definitions and hence have no meaning or value in a protocol or interactive environment. Some variables are only useful in Prism applications.

6.1  The STORE SETTINGS Command

Sometimes you may want to set some variables for a protocol, but be able to restore them to their original values after the protocol has executed. For example, you may want to SET NOECHO to ensure that the commands in the protocol are not displayed as they are issued but return the user's original setting, whether it was ECHO or NOECHO, when the protocol is done. The easiest way to accomplish this is with the STORE SETTINGS command.

The STORE SETTINGS command tells SPIRES to save the current values of the following flag variables:

In addition, the global flags set by the SET WRITE/NOWRITE and SET READ/NOREAD commands are also included. [EXPLAIN SET READ COMMAND or EXPLAIN SET WRITE COMMAND for more information on these Device Services commands.]

[DEFQLOAD and the READ and WRITE settings don't show up in SHOW SETTINGS.]

To restore the variables to their state when the STORE SETTINGS command was issued, issue the command RESTORE SETTINGS. The settings are only stored during the current session -- if you leave SPIRES, the stored settings will be discarded.

Nesting of stored settings is allowed. For instance, if a STORE SETTINGS command is issued and then another STORE SETTINGS command is issued before a RESTORE SETTINGS command, the second group of settings will be stacked on top of the first. Then a RESTORE SETTINGS command would restore the second group, and another RESTORE SETTINGS command would restore the first group.

6.2  Variable Types

System variables, like user-defined variables, are classified according to the type of value they contain: flag, integer, string, character, hex, line, and new (for those variables used in triples). A number of functions are available for variable manipulation. The $CHARACTER, $STRING, $INTEGER, $NEGATIVE, $PACKED, $REAL, $LINE, $FLAG, $HEX, and $WDS functions convert a value to the specified type if possible. The $TYPETEST function allows you to test whether the value can be successfully converted to a particular type. The $TYPE function tells you what type a value is.

Each type is discussed more fully below.

6.2.1  (Flag Variables)

Flag variables are variables whose values are either "1" (for TRUE, or ON) or "0" (for FALSE, or OFF), and act, as their name implies, as flags indicating that a certain condition exists. Some flag variables come in pairs, so that one is true when the other is false, and vice versa. These variables are set with the SET command. For example, the SET ECHO command sets the $ECHO variable to TRUE, also setting the $NOECHO flag to FALSE. The SET NOECHO command reverses this. The other flag variables are set by specific conditions occurring, as for example, the $SELECTED flag is set to TRUE whenever you select a subfile.

A flag variable can be tested in one of two ways. The following IF commands are equivalent and test whether a subfile is selected, i.e., the $SELECTED flag has been turned on.

The following commands test if no subfile is selected:

The SHOW EVAL command always returns a "1" or "0" when used to display the value of a flag variable.

6.2.2  (Integer Variables)

Integer type system variables return 4-byte integers when used in variable expressions.

6.2.3  (Character/String Variables)

Character/string type variables return a varying-length string or a fixed-length character value. The difference between character (CHAR) and string (STRING) variables is minor, but can be significant in some variable tests. A variable of type CHAR has a fixed length and when that variable is assigned a value, the value is padded with blanks to create a value of the assigned length. A variable of type STRING is the length of the value currently assigned to it.

In an IF statement that compares two values of differing types, the variable on the left side of the equation determines the type that the variable on the right will be converted to in the comparison, since only values of the same type may be compared. When a CHAR value is converted to a STRING value, it will retain the trailing blanks it had when it was a CHAR value. When a STRING value is converted to a CHAR value, it remains the same length it was as a STRING value, and does not get padded with blanks to fill out the length of the CHAR equivalent.

For example, $TERMINAL is a four-character CHAR type variable. If you issue the command

the $TERMINAL variable actually hold "V77 " -- a four character value. When the following IF statement executes

the message will NOT be written to the screen, because the comparison is actually being made between "V77 " and "V77". Because $TERMINAL is a CHAR type variable, the value on the right side of the equation is being converted to CHAR before the comparison is made, but not being padded with blanks. But in this case,

the message will be sent because V77 is a STRING value 3 characters long, and $TERMINAL is being converted to a STRING variable, removing any trailing blanks, so the comparison is between "V77" and "V77".

6.2.4  (Line Variables)

Line variables hold values that represent line numbers.

6.3  Variables Classified by Function

The following chart categorizes variables according to the type of information that they provide, making it easy for you to locate a variable to fill a particular need. The variables are then listed alphabetically and briefly described. The descriptions here are intended as a reference. The practical applications of many of them are described elsewhere in the manual; cross-references to those sections are provided. For example, the $YES, $NO, and $ELSE variables hold information about command success and failure, and their use is subsumed under the use of the IF, THEN, and ELSE commands, so the full discussion of how to use these variables in a protocol environment is given in the section describing those commands.

System and Error messages

$CHRX, $CHRI                           $MSGNUM ($MNUM)
$ELOGSCNT, $ELOGWCNT                   $NOLEV
$ENUM, $SNUM, $PSNUM                   $SERX, $SERI
$ERRCODE ($MSGCODE, $MCODE)            $SORTCODE
$INFOX, $INFOI                         $UCODE ($USERCODE, $UCD)
$MSGLEV                                $WARNX, $WARNI

Environment

$ACCOUNT                               $MULTILOG
$AREANAME                              $NAME
$BIN                                   $PATHCUR
$CLEAR/$NOCLEAR                        $PROGRAM
$CRTMODE                               $RECSEP
$CPUTIME                               $SUPERMAX, $SUPERVAL
$DATE, $DATECC                         $SYSTYPE
$DEFQLOAD                              $TERMINAL
$DIAG/$NODIAG                          $TERMTYPE ($TTYPE)
$ECHO/$NOECHO                          $TIME
$ENVIRON                               $TIMER
$ETIME                                 $UDATE
$GCODE ($GRO)                          $UPPER/$UPLOW
$INTXEQ                                $USER ($USE)
$IOCOUNT                               $USERNAME
$JOBNUM                                $UTIME
$LENGTH                                $VERSION
$LIST/NOLIST                           $WIDTH
$MAIL

Selected subfile, file, records, format

$DISCHR                                $MINCHR
$FILENAME                              $NEXTSLOT, $NEXTSLOTKEY
$FILEDATE                              $SELCHR
$FILETIME                              $SELECT
$FORMAT                                $SELECTED
$FRAME                                 $SUBFSIZE
$GETSPATH, $GETXPATH                   $SETFORMAT
$GLOFORMAT                             $SLOT/NOTSLOT
$GOALREC                               $TCOMMENT
$KEY                                   $TRANSFER

Result/Stack

$LASTRESNUM                            $RESNUM
$RELPOS                                $RESULT
$RESCNT                                $SRCHSTAT ($FINDSTAT)
$RESHIST                               $STACK
$RESNAME                               $ZRESULT/$NZRESULT

Global FOR/Partial FOR

$FORTYPE ($FORTYP)                     $PRTCNT
$GXCOUNT, $GPCOUNT                     $PRTLVL
$PATHKEY                               $PXCOUNT, $PPCOUNT

Protocol Execution

$ASK                                   $SEED
$COMMAND                               $SEP
$COMPXEQ                               $STOP/$NOSTOP
$CURCMD                                $SUBLATCH
$ELSE                                  $TIMELIMIT
$LASTXEQ ($XEQLAST)                    $TRUE/$FALSE
$PARMCNT                               $UPDTYPE
$PATHNUM                               $XEQ
$PRECMD                                $XEQLVL
$PROMPT                                $XTRACE
                                       $YES/$NO

Active File

$ACTNUM ($ACTNO)                       $WDSL ($WDSLINE)
$AREANAME                              $WDSR
$DELTA                                 $WDST
$NEXTWDSR                              $WDSW
$NEXTWDSW

Prism

These variables are documented in the manual "Prism Applications."

$CRTCNT               $NEXTCMD         $RECID         $UNIVEMAIL
$DEBUG                $PARM (*)        $REPTNAME      $UNIVID
$DESTINATION          $PRISMID         $SCRNAREA      $UNIVNAME
$DESTVAL              $PROXYEMAIL      $SCRNSTAT      $UNIVPHONE
$EVENTZONE            $PROXYID         $SRCHTYPE
$FIELDVAL             $PROXYNAME       $SRCHVAL
$FORMNAME             $PROXYPHONE      $STATUS
$LASTCMD
$MAINCMD
$MSGLINE
(*) - The SPIRES variable $PARM has special uses in Prism.

Triples

$ANY
$NEW

6.3.1  Variables Used Primarily in Formats

These variables are documented in the manual "SPIRES Formats."

Format Execution and Buffer Control

$ABORT                                 $LABEL ($L)
$ATTN                                  $OVERFLOW
$FLINES                                $PARM
$FLUSH                                 $SETFORMAT
$FORMAT                                $SKIPF
$FRAME                                 $SUPPRESS
$FTRACE                                $TESTSUBG
$GLOFORMAT

Single Record Output

$CCOL                                  $LROW
$CROW                                  $NCOLS
$CUROCC ($CURROCC)                     $NROWS
$ELEMID                                $SCOL
$ELOCC                                 $SROW
$LASTOCC                               $REFERENCED
$LCOL                                  $TESTREF
$LOOPCT                                $TRUEOCC

Value Processing

$CLEN ($VLEN)                          $PVAL
$CVAL                                  $ULEN
$DEFAULT                               $UVAL

Multiple Record Output

$FREC                                  $LRGLCTR
$LRGNXT                                $RECNUM ($RECNO)

Input Frames

$ENDATA                                $STARTCHAR
$ENDCHAR                               $TESTFAIL
$SKIPEL

Processing Errors

$APROCERR                              $PROCERR
$GPROCERR                              $WPROCERR

Full Screen Formats

$EROW                                  $PROTECT
$ECOL                                  $REPROMPT
$CHANGED                               $TERMPAD
$GCHANGED
$GNCHANGED
$NODATA

Sorting

$SORTDATA                              $SORTKEND
$SORTDEND                              $SORTKEY

Report Mode Processing

$COLUMNS                               $MARGIN
$COLWDTH                               $NEWCOL
$CURCOL                                $NEWPAGE
$FTRLEN                                $NOBREAK
$HDRLEN                                $PAGECTL
$LINEPP                                $PAGENUM ($PAGENO)
$LLEFT                                 $REPORT/$NOREPORT
$LREC

6.3.2  Variables Allowed Only in User Defined Processing Rules

These variables are documented in the manual "SPIRES File Definition."

$PASSDEL                               $SUBCODE
$PROCVALUE ($PROCVAL,$VAL)             $VELEM
$PTRACE                                $VOCC
$P1

6.4  Variable Descriptions in Alphabetical Order

6.4.2  ($ACCOUNT)

$ACCOUNT is a character variable that contains a six-character value of the form gg.uuu, corresponding to the user's current account number. It may not be modified with a SET command.

6.4.3  ($ACTNUM ($ACTNO))

$ACTNUM (aka $ACTNO) is an integer variable corresponding to the user's current WYLBUR active file. It may not be modified with a SET command.

In batch SPIRES, its value is always 1.

6.4.4  ($ASK)

$ASK is a string variable that contains the response to the most recently executed ASK or WDSR command or the parameters passed with the .. command. You can also use the SET ASK command [See 5.4.] to set the value of $ASK.

When $ASK is set by the SET ASK command, its length is limited to 256 characters maximum. In addition, an end-user's response to the prompt issued by the ASK command is limited to 160 characters maximum.

Passing Parameters to $ASK with the .. Command

The ".." or "..protocol.name", used to invoke execution of the protocol it names, can pass parameters to the variable $ASK. [See 2.2.] If parameters are passed to $ASK in this way, the protocol must use and/or save the contents of $ASK before any other command is executed that would change the value of $ASK; otherwise, the parameters are lost.

The length of $ASK may be larger than 256 characters when it is set by the ".." command (or by a SPIRES macro command).

For example:

 -> ..protocol.name  1  2  3  <-- passes the parameter string
 -> show eval $ask                '1  2  3' to $ASK
 1  2  3
 -> xeq from protocol.name    <-- does not alter $ASK
 -> show eval $ask
 1  2  3
 -> ..protocol.name           <-- sets $ASK to null since no
 -> show eval $ask                parameters were specified

 ->

6.4.5  ($AREANAME)

$AREANAME is a string variable that contains the current area name as requested by the "IN areaname" prefix of the current command. If no "areaname" prefix is in effect, the variable is null.

For example, if the current command is IN ACT DISPLAY ALL, the value of $AREANAME will be "ACTIVE". (Note that the value of the variable will always be spelled out as ACTIVE for the active file, even if an abbreviation was used in the command.) If the current command is IN SCREEN XEQ GLOBAL FRAME SCAN, then the value of $AREANAME will be "SCREEN".

The IN-AREA statement in formats also affects $AREANAME for the duration of the execution of the frame containing it.

Note that Prism maintains a $SCRNAREA variable, that contains the name of the currently active area in a Prism application. Since Prism does much of its work by means of formats and protocols executing in device services areas, $SCRNAREA and $AREANAME often have the same value.

6.4.6  ($BIN)

$BIN is a character variable that represents the bin number specified in the file definition for the selected subfile.

If BIN=HOLD was specified, then the value of $BIN is 999; if BIN=PURGE was specified, then the value of $BIN is 000. If no bin statement was specified, then the value of $BIN is the character form of hexadecimal zeroes, i.e., $RETYPE($HEX(000000),CHAR).

6.4.8  ($CHRX and $CHRI)

$CHRX and $CHRI are integer variables that control the presentation of SPIRES data base charging messages. They are set by a command of the form:

where "number" is 0, 1, 2, 3, or 4 as described for the SET MESSAGE command. [See 5.9.]

$CHRX is set by protocol commands, the 'X' suffix denoting protocol XEQ, or with the WITHIN XEQ prefix. $CHRI is set by user-issued commands, the 'I' suffix denoting 'interactive', or with the WITHIN INTERACTIVE prefix.

6.5.0  ($CLEAR/$NOCLEAR ($CLR/$NOCLR))

If $CLEAR (alias $CLR) is TRUE, SPIRES automatically clears the active file before sending data to it, unless overriding options such as CONTINUE are specified. If $NOCLEAR (alias $NOCLR) is TRUE, you are always asked 'OK to clear?' before SPIRES sends data to the active file if the active file contains anything.

The SET CLEAR (or SET CLR) and SET NOCLEAR (or SET NOCLR) commands are used to set these flags. The SHOW SETTINGS command displays which one is currently set. The default is NOCLEAR.

6.5.2  ($COMMAND)

$COMMAND is a character variable that returns a three-character string corresponding to the first three characters, including blanks, of the preceding command issued in either protocol or command mode.

6.5.4  ($COMPXEQ)

$COMPXEQ is a string variable that returns the name of the currently set COMPXEQ subfile. This may be different from the currently selected subfile.

6.5.6  ($CPUTIME)

$CPUTIME is an integer variable that contains the amount of CPU time in ORVYL (in ten-thousandths of a second) used since you logged on. It can be used when debugging applications to help you find any particularly inefficient parts of the operation. It can be expensive to use, adding its own weight to your CPU costs.

The value returned is equivalent to what ORVYL maintains and displays when you logoff. If the value returned is 15723, for example, it means that you have used 1.5723 seconds of ORVYL CPU since logging on or calling SPIRES.

6.5.7  ($CRTMODE)

$CRTMODE is a flag variable that's set to $TRUE (1) when the current session is in full-screen mode; in line-by-line mode, the value of $CRTMODE is 0.

6.5.8  ($CURCMD and $PRECMD)

$CURCMD is a character variable that corresponds to the first three characters of the command currently being processed by the system. This variable is very useful in SPIRES formats, as it can be used to determine what command initiated format processing. The variable $COMMAND [See 6.5.2.] would have the first three characters of the command that preceded the command that initiated format processing. (Note that in SPIBILD, $CURCMD is equivalent to $COMMAND. Both are established by command input or by internal commands within a batch input stream, such as "ADD;" or "MERGE;".)

If the command being executed was issued as IN ACTIVE DISPLAY or USING frame ADD, then $CURCMD would contain DIS or ADD. The command prefixes IN ACTIVE and "USING frame" would be contained by the $PRECMD variable, which contains the prefix string, up to the actual command stem. Like $CURCMD, $PRECMD is most useful during format processing.

Note that both $CURCMD and $PRECMD are set to null when the current command is finished executing. However, $PRECMD is copied to $PREMAC if the command is a protocol execution command like ..name or XEQ FROM or a system macro call. $PREMAC gives you access to the calling command prefix at the start of protocol execution. If needed, you should make use of $PREMAC within the protocol before it is destroyed by another protocol execution command. Both $PRECMD and $PREMAC are limited to 64 characters.

6.5.9  ($DATE, $DATECC and $UDATE)

The $DATE variable returns an eight-byte CHAR value of the form MM/DD/YY. The $DATECC variable returns a 10-byte CHAR value of the form MM/DD/CCYY. Neither may be altered by a SET command.

The $UDATE variable is the current date in its unconverted form. The $DATEOUT function can be applied to it to give you the current date in a number of attractive forms other than the one shown for $DATE and $XDATE. [See 7.2.]

6.6.0  ($DEFQLOAD)

If $DEFQLOAD is TRUE, then commands such as SET FORMAT, ALLOCATE, and RESTORE STATIC will access the deferred queue of the appropriate system file in attempting to locate the format, vgroup, or static group. If $DEFQLOAD is FALSE, then the deferred queue is not accessed, which saves time in production environments.

The SET DEFQLOAD and SET NODEFQLOAD commands are used to set this flag. Note that there is no $NODEFQLOAD variable. By default, $DEFQLOAD is TRUE.

6.6.2  ($DELTA)

$DELTA is a line variable that contains the current value of the WDS line increment. This variable may be set with the SET DELTA command. Caution: all SPIRES sessions begin with a delta of 1.0; if you set delta to something other than 1 in Wylbur before entering SPIRES, the delta you set will not be reflected in the value of $DELTA. If you have Wylbur set in SPIRES, you must preface the SET DELTA command with a "/" so that the value will be reflected in $DELTA.

6.6.4  ($DIAG/$NODIAG)

The $DIAG and $NODIAG system flags are no longer used by the system. You may use these flags for your own purposes.

The SET DIAG and SET NODIAG commands are used to set these flags. The SHOW SETTINGS command shows which of the pair is currently set. The default is DIAG.

The SET DIAG and SET NODIAG commands were superseded by the various forms of the SET MESSAGE command.

6.6.8  ($ECHO/$NOECHO)

If $ECHO is TRUE, the system displays each command of a protocol as it is executed. If $NOECHO is TRUE, commands are not displayed, although informational and diagnostic messages are still displayed. If a command is prefixed with a '!', it is not echoed regardless of the setting of $ECHO/$NOECHO. [See 5.6 for more information about using these variables.]

The SET ECHO and SET NOECHO commands are used to set these flags. The SHOW SETTINGS command shows which of the pair is currently set. The default is ECHO.

6.6.9  ($EDITOR (not applicable to Stanford environment))

6.6.9b  ($ELOGSCNT, $ELOGWCNT)

The $ELOGSCNT variable contains the number of rows in the error log that describe Serious errors. The $ELOGWCNT variable contains the number of rows that describe Warning errors. [See 8.4.3.]

6.7.0  ($ELSE)

The $ELSE flag is TRUE if the condition in the preceding IF statement was evaluated as false and FALSE if the condition was true.

For example:

would send the message 'Number is not six' to the terminal. [See 5.8.]

The THEN and ELSE commands have superseded most uses of the $ELSE flag. [See 5.8.1.] They are considered easier to use:

6.7.2  ($ENUM, $SNUM, and $PSNUM)

$ENUM, $SNUM, and $PSNUM are integer variables containing the number of the last error code(s) generated.

For example, after the following error message has been issued

the $ENUM variable contains 35, the $SNUM 261. They will not change until another error occurs.

The $PSNUM variable contains the previous value of $SNUM. This variable can be handy in cases where two errors have occurred in a process and you need to know about both errors. For example, if you have issued an ADD request and a format error S854 occurs, followed by an S1 error, then $SNUM has a value of 1 and $PSNUM has a value of 854.

The value of $PSNUM changes only when $SNUM changes. So, for example, if several S1 errors occur in a row, $PSNUM will not change from the value it had when the first S1 error occurred. The CLEAR MESSAGE NUMBER command sets the value of both $PSNUM and $SNUM to 0.

These variables can be used to issue an explanation of the error from the EXPLAIN file while still in protocol mode by issuing a command as follows:

6.7.4  ($ENVIRON ($ENV))

$ENVIRON (alias $ENV) is an integer variable that represents the particular SPIRES environment being used; its value is either 1, 2, or 3.

   $ENVIRON        SPIRES Environment
   --------        -----------------------------------------------

       1           Online SPIRES or SPIBILD, either from a real
                   terminal or from a virtual terminal (BATWYL)

       2           Online SPIRES through a batch program; usually
                   this means you are using the Host Language
                   Interface.  (An overnight SPIBILD run also would
                   return a value of 2 for $ENVIRON.)

       3           Batch version of SPIRES or SPIBILD, going through
                   OVAM; usually means that you are using FASTBILD
                   or have submitted a job using the OFFLINE
                   facility.

6.7.6  ($ERRCODE ($MSGCODE, $MCODE))

$ERRCODE (alias $MSGCODE or $MCODE) is an 8-byte character variable that contains the number and level of the last error message generated. For example, in message level 3, you might get the following message from the system after typing the command ENDFOR:

The value of $ERRCODE in this case is "-063(I)-".

The $MSGNUM variable (alias $MNUM) contains the integer portion of the error code, in this case 63.

SHOW SUBFILE DESCRIPTION SYSTEM MESSAGES to find out more about the message codes that are used.

6.7.7  ($ETIME)

The $ETIME integer-type variable measures, in milliseconds, the elapsed time of a session since the time of logging on:

  -> show eval $etime
  421751                                   <--(in milliseconds)

The display of $ETIME can also be converted into various different formats using the $TIMEOUT function [See 7.2.] as in the example below:

  -> show eval $timeout($etime,14)
  421.751 Seconds

$ETIME can be useful for measuring the sometimes varying amount of time an activity takes in an application. One of the many possible ways to do this is shown below:

  Let PreValue = $Etime
      :                  <--(activity whose elapsed time is measured)
      :
  Let SecsElapsed = $TimeOut($ETime - #PreValue,14)
  Show Eval #SecsElapsed || ' Elapsed.'

6.7.8  ($FILEDATE and $FILETIME)

$FILEDATE is a character variable that represents the most recent date on which the file holding your currently selected subfile was compiled. The variable is an eight-byte character value in the form MM/DD/YY (e.g., 11/28/85).

The $FILETIME variable represents the most recent time that the file holding your currently selected subfile was compiled. The variable is an eight-byte character value in the form HH:MM:SS (e.g., 08:20:28).

6.8.0  ($FILENAME)

$FILENAME is a string variable that contains the name of the SPIRES file currently selected. The value is taken from the FILE statement of the file definition and takes the form "gg.uuu.filename". When no subfile is selected, the value is null.

6.8.2  ($FINDSTAT ($SRCHSTAT))

$FINDSTAT (alias $SRCHSTAT) is an integer variable that can be tested to determine whether one of several unusual searching situations occurred.

    $FINDSTAT     Search Condition
    ---------     -------------------------------------------------
       1          the search found an indexed value for which there
                  are no pointers

       2          the search value contained only exclusion terms,
                  resulting in the error message "Request has no
                  content"

       4          the user terminated a search command by
                  responding "no" to the prompt "Continue search?"

       8          you retrieved an out-of-core result

If none of these conditions occurs, the value of the variable is 0.

Here are some examples of its use:

Because the word "the" appears in the exclusion list of the NAME index, the search request "had no content."

The name "Beethoven" did appear in the index, but there were no pointer values in the sub-index for anyone named "Horace Beethoven".

6.8.4  ($FORMAT)

$FORMAT is a string variable that contains the name of the currently set format; if no format is set, this variable is null. Note that this is only the name given in the FORMAT-ID statement of the format definition; it does not contain any account-number prefix, as do $SETFORMAT and $GLOFORMAT.

6.8.6  ($FORTYPE ($FORTYP))

$FORTYPE (alias $FORTYP) is an integer variable that always contains a non-zero value when a Global FOR class is in effect. It has a value of zero when no Global FOR class is in effect. When a Global FOR class is in effect, $FORTYPE will have one of the following values:

    $FORTYPE                 Global FOR class
    --------                 ----------------
        4                    SUBFILE
        8                    TREE
       12                    DEFQ
       16                    UPDATES
       20                    REMOVES
       24                    ADDS

6.8.8  ($FRAME)

$FRAME is a string variable containing the name of the frame name in either the "USING frame" command prefix or the "XEQ FRAME" command. If no frame is named, this variable is null.

6.9.0  ($GETSPATH/$GETXPATH)

These two variables hold the structural occurrence map from the most recently executed $GETxVAL, $GETVOCC, $LOOKSUBF, $LOOKSUBG, or $LOOKSYS function. [See 7.2.] $GETSPATH is a string variable in the form

where each integer value represents the occurrence number of each structure in the route taken to retrieve the element. For example, "0001.0002.0000" represents the second occurrence of the first structure, and within that structure, the third occurrence of the next structure, and within that structure, the first occurrence of the next structure, irrespective of any filtering that might be in effect. $GETXPATH is the hexadecimal form of this value, with each occurrence number a fixed 2-byte integer. So the value in the above example would be represented in $GETXPATH as "000100020000".

If these variables are null, then the default value of the function was returned or an error occurred. They cannot be explicitly set. [For more information, see 7.2; for a full explanation of how the structural-occurrence-map is used with file-definition Userprocs, see the manual "SPIRES File Definition".]

6.9.2  ($GLOFORMAT)

$GLOFORMAT is a string variable that contains the fully-qualified name (including the account prefix) of the currently set global format. If the global format is a system-owned format, only the name of the format preceded by the "$" is returned. Note that the $FORMAT variable still holds the name of any file format, including a general file format such as $PROMPT or $REPORT. (Both a file format and a global format can be set at the same time.)

For example,

6.9.4  ($GOALREC)

$GOALREC is a string variable containing the name of the goal record of the currently selected subfile, or the name of the subgoal record during subgoal processing. The value will be null if there is no subfile.

6.9.6  ($GXCOUNT, $GPCOUNT, $PXCOUNT, and $PPCOUNT)

The $GXCOUNT, $GPCOUNT, $PXCOUNT, and $PPCOUNT are integer variables that can be used in Global or Partial FOR. They provide some of the values shown by the SHOW LEVELS command. They represent:

The $GXCOUNT and $GPCOUNT variables have the value "0" if no Global FOR processing has occurred since the current subfile was selected; when you leave Global FOR, these variables retain the values they last had when in Global FOR. The $PXCOUNT and $PPCOUNT variables have the value -1 when Partial FOR is not in effect.

The following example demonstrates the use of $GPCOUNT and $GXCOUNT.

6.9.7  ($IDATA (not applicable to Stanford environment))

6.9.8  ($INFOX and $INFOI)

$INFOX and $INFOI are integer variables that control the presentation of SPIRES informational messages. They are set by a command of the form:

where "number" is 0, 1, 2, 3, or 4 as described for the SET MESSAGE command. [See 5.9.]

$INFOX is set by protocol commands, the 'X' suffix denoting protocol XEQ, or with the WITHIN XEQ prefix. The $INFOI is set by user-issued commands, the 'I' suffix denoting 'interactive', or with the WITHIN INTERACTIVE prefix.

6.9.9  ($INTXEQ)

$INTXEQ is a flag variable whose value is 0 if commands are being issued within a protocol (in "XEQ mode") or 1 if commands are being issued interactively.

6.10.0  ($IOCOUNT)

$IOCOUNT is an integer variable holding the sum of ORVYL input-output (I/O) operations for the current session, starting from the point of logging on. The purpose of the variable is to help you track your application's efficiency in using system resources: other things being equal, the lower your I/O-count, the more efficiently you're using the system.

Note that unlike CPU counts, I/O counts remain stable for a given procedure, even at different time-blocks on the computer, so they can help establish repeatable "benchmarks" for your application. (By the way, the I/O's that are counted by this variable are those in which the machine attaches a remote device such as a disk for reading -- these are points that can conceivably act as bottlenecks within the system. Other kinds of input and output, such as to a terminal or printer, are not counted, nor is WYLBUR I/O counted.)

For example:

  -> Select Paperbacks
  -> Show Eval $IOCount
  44
  -> Set Format Read
  -> Show Eval $IOCount
  50

Setting the "Read" format above apparently required six I/O's.

You can use your own variables to trap I/O usage at virtually any point in your application:

  -> let prevcount = $iocount
  -> ..procedure
  -> let taskcount = $iocount - #prevcount
  -> show eval ' The protocol required ' #taskcount ' I/O.'
  The protocol required 22 I/O.

Other commands for tracking I/O include the SET SINFO and SHOW SINFO commands described in "SPIRES Technical Notes".

6.10.1  ($JOBNUM)

$JOBNUM is a character variable that contains the four-digit job number of a job submitted using a FILE area and the JOB option on the ASSIGN AREA command. [See the Device Services manual for more information about job submission.]

6.10.2  ($KEY)

$KEY is a string variable that contains the value of the last record key used by SPIRES in the current session.

6.10.3  ($LASTRESNUM)

When the user has SET RESULT HISTORY in effect, the $LASTRESNUM variable contains the number of the last history item saved. ($RESNUM, the number of the history item associated with the current search result, may be lower than $LASTRESNUM, if, for example, BACKUP commands have been issued.) The history is controlled by the SET RESULT HISTORY command.

The value is 0 if the history has just been turned on and no searches have been done yet. If result history is not set, then the value of this variable is -1.

6.10.4  ($LASTXEQ ($XEQLAST))

$LASTXEQ (alias $XEQLAST) is a string variable that contains the last command executed, or the last command the system attempted to execute. This is a very helpful debugging variable, but is available only during a break in protocol execution, as shown below.

If an unexpected XEQ break occurs in a protocol in which NOECHO has been SET, you may ascertain which statement caused the XEQ break by issuing a SHOW EVAL $LASTXEQ command:

(Note: the command was illegal because the SET command does not allow expressions.)

6.10.6  ($LENGTH ($LEN))

$LENGTH (alias $LEN) is an integer variable that contains the current WYLBUR line length. It may be modified using the SET command. Caution: all SPIRES sessions begin with $LENGTH set to 72; if you set length to something other than 72 in WYLBUR before entering SPIRES, the length you set will not be reflected in the value of $LENGTH.

The SET LENGTH command in SPIRES is independent of the SET LENGTH command in WYLBUR. If you have SET WYLBUR or used the "(W)" parameter on the CALL SPIRES command, you must precede the command with "/" to pass it on to SPIRES.

6.10.8  ($LIST/$NOLIST)

If $LIST is TRUE, the system automatically lists data sent to the active file, e.g., with a TRANSFER command. If $NOLIST is TRUE, data sent to the active file is not listed.

The SET LIST and SET NOLIST commands are used to set these flags. The SHOW SETTINGS command shows which of the pair is currently set. The default is LIST.

6.11.0  ($MAIL)

The $MAIL variable allows you to determine if SPIRES mail is waiting. Generally, the variable is set or reset whenever you issue a CALL SPIRES or a SHOW SPIRES MAIL command. Its value is TRUE if mail is waiting, FALSE if there is no mail.

Note: for the sake of efficiency, SPIRES does not check for mail, and does not set or reset the $MAIL variable, when the CALL SPIRES command uses both the "Q" parameter and the : (colon) parameter. [See the document "SPIRES Searching and Updating" for information on these parameters.]

6.11.2  ($MSGINT)

$MSGINT is an integer variable that is used by the system in the generation of some error messages. Its value cannot be set by the user.

6.11.4  ($MSGLEV)

$MSGLEV is a character variable that returns a single-character corresponding to the level of the last message sent.

   $MSGLEV   Message Level
   -------   ---------------------
      I      informational message
      W      warning message
      S      serious message

The value of $MSGLEV is kept current even if messages to the terminal have been turned off using the SET MESSAGE command. The $MSGLEV variable is not setable.

6.11.6  ($MSGLIT)

The $MSGLIT variable is used by the system in the generation of some error messages. Its value cannot be set by the user.

6.11.8  ($MSGNUM ($MNUM))

$MSGNUM (alias $MNUM) is an integer variable that contains the integer portion of the last error code generated. For example, in message level 3, you might get the following message from the system after typing the command ENDFOR:

The value of $MSGNUM in this case is 63.

The $ERRCODE variable contains this number plus the level of the error, in this case "-063(I)-".

SHOW SUBFILE DESCRIPTION SYSTEM MESSAGES to find out more about the message codes that are used.

6.12.0  ($MULTILOG variable)

$MULTILOG is an integer variable that lets you distinguish between multiple logged-on sessions for a single account.

The value of $MULTILOG corresponds to the number appended to I.T.S. accounts in the SHOW LINES display, except that for the "primary" logged-on account (the first user to have logged on to that account) $MULTILOG is 1. For instance, in the display below, the user on line 41 would have a $MULTILOG value of 1, while the user on line 178 would have a $MULTILOG value of 2:

Even after the primary user AF.ISH ends his session, AF.ISH#2 will still have a $MULTILOG value of 2, until he or she logs off.

6.12.2  ($NAME)

$NAME is a 6-byte character variable that contains the user's account number in the form uuu$gg. It may not be modified by a SET command.

6.12.4  ($NEXTSLOT and $NEXTSLOTKEY)

$NEXTSLOT is an integer variable containing the value of the next slot number to be assigned in a slot goal record type (i.e., the next slot number to be assigned to a record in the selected subfile). If the record type is not a slot record type but does have an element whose value is generated uniquely by action A125, $NEXTSLOT will contain the next unique value. (Note that if multiple users are adding records to a subfile, the value of $NEXTSLOT may change between the time you examine it and the time that you actually add a record; thus, in that case, the value of $NEXTSLOT that you see may not be the value assigned as the key, or unique value, for your record.)

If the record type is not slot, or if no element has its value generated by A125, the value of $NEXTSLOT is 0. If no subfile is selected or no file attached, the value of $NEXTSLOT is -1. If an error occurs when SPIRES retrieves the value, the value is set to -2. An error would only occur when a system file error occurs, which is very rare.

$NEXTSLOTKEY is available when the record-type has a slot key with check digit processing. It shows the value of the next slot key, including the check digit, which is not part of $NEXTSLOT. This is important when you need to lock a record with the SET RECORD LOCK command. [EXPLAIN SET RECORD LOCK COMMAND for further information.]

6.12.6  ($NEXTWDSR)

$NEXTWDSR is a line variable that contains the line number of the line that will be read by the next WDSR command. It is incremented by .001 each time a line is successfully read.

It can be set with the SET WDSR command. [See 5.13.] If you specify a line number on the SET WDSR command with string or occurrence criteria (e.g., "the third occurrence containing the string 'mudpie'") and such a line does not exist in the active file, $NEXTWDSR is set to 0. An explicit line reference (i.e., "300" or "#LINE" where LINE = 300) will always set the $NEXTWDSR variable, even though that line number might not exist in the active file.

If a program only needs a line number for a statement (such as the Wylbur CHANGE command), and not the line contents, it is not necessary to read the line with a WDSR command. [See 11.8.]

6.12.8  ($NEXTWDSW)

$NEXTWDSW is a line variable that contains the line number of the line that was most recently written by the WDSW command. It is incremented by the value of $DELTA each time a line is written.

It can be set with the SET WDSW command. [See 5.13.] If you specify a line number on the SET WDSW command with string or occurrence criteria (e.g., "the third occurrence containing the string 'mudpie'") and such a line does not exist in the active file or there is no active file, $NEXTWDSW is set to $DELTA. An explicit line reference (i.e., "300" or "#LINE" where LINE = 300) will always set the $NEXTWDSW variable, even though that line number might not exist in the active file.

If a program only needs a line number for a statement (such as the Wylbur CHANGE command), it is best to use the SET WDST command.

6.12.8.1  ($NO)

See under $YES/$NO.

6.13.0  ($NOLEV ($NOL))

$NOLEV (alias $NOL) is an integer variable that is set by SPIRES each time a message is generated by the system. Its value is one of the following:

   $NOLEV   Type of Error
   ------   ---------------------------
     0      informational message
     4      warning message
     8      serious message

The value cannot be set by the user.

6.13.1  ($ODATA (not applicable to Stanford environment))

6.13.1a  $PARM

$PARM is a string variable containing the parameter list specified on a SET FORMAT, SET GLOBAL FORMAT or SELECT command. It provides a simple way for the user to pass parameter values into a format or a subfile's select commands. For example:

The value of $PARM would become "(ring) name phone.number". $PARM is reset to null when the format is cleared.

Parameter lists on SELECT commands are useful when the subfile definition has SELECT-COMMAND statements that may be executed. For example:

When no parameter list is given on a SELECT command, $PARM is set to null. Otherwise, $PARM contains the parameter list following the subfile name and comma. The value of $PARM after the SELECT command above would be "1ABC999".

When $PARM is set by the SET FORMAT, SET GLOBAL FORMAT or SELECT command, its allowed length is virtually unlimited. It may be set explicitly during format execution with the SET PARM Uproc or the SET PARM command, in which case its value is restricted to 256 characters or less:

If you need to use the parameters passed to a format on a SET FORMAT command, it is recommended that you save the value of $PARM in a user variable by coding a LET Uproc in a startup frame. Because the variable is reset by either a SET FORMAT, a SET GLOBAL FORMAT or a SELECT command, any of which may be issued at any time, its value might change between the time the SET FORMAT (or SET GLOBAL FORMAT) command is issued and the format is executed.

Likewise, if your subfile's SELECT-COMMANDs include a SET FORMAT command, be careful to save the value of $PARM each time it is reset, if you need to use those values. That is, a $PARM value set by a parameter list on the SELECT command will be reset by the SET FORMAT command in your SELECT-COMMANDs.

6.13.1b  ($PARMCNT)

$PARMCNT is an integer variable that contains the number of parameters stored by the most recent $SETPARMS function. Its range is 0 to 255. It is cleared to 0 after a $GETPARMS function is executed. Its value is also 0 following an unsuccessful use of the $SETPARMS function. [See 4.5.1.]

6.13.2  ($PATHCUR)

$PATHCUR is an integer variable that contains the number of the current path in use during a SPIRES session. The value will be 0 if the session is currently on the primary path.

6.13.3  ($PATHKEY)

$PATHKEY is a string variable containing the key value of the index record currently being handled under FOR INDEX processing. (See the discussion of FOR INDEX in the Global FOR manual for more details.)

6.13.4  ($PATHNUM)

$PATHNUM is an integer variable containing the number of the path referred to in the most recent command that opened a path or that began with the THROUGH prefix. (See Technical Notes, Chapter 14 for a complete discussion of path processing.)

6.13.6  ($PROGRAM)

$PROGRAM is a string variable that contains the name of the program or application currently executing. Some possible values for $PROGRAM are: SPIRES, SPIBILD, FASTBILD, FOLIOSOC, PRISM and PRISMNC. (This is by no means an all-inclusive list of possible values.)

$PROGRAM acts in cooperation with the PROGRAM statement in a subfile's file definition. (The PROGRAM statement, when it is coded, limits a user's access of the subfile to the programs it specifically names, e.g., PRISM.) If the PROGRAM statement has been coded in a subfile's file definition, and a user attempts to select that subfile, the value contained in $PROGRAM will first be compared to the value or values authorized by the PROGRAM element. If no match is found between programs, the select will fail and the user will receive the message "Program error".

6.13.8  ($PROMPT)

$PROMPT is a string variable that contains the string that will be used as the prompt when an ASK command is issued if no PROMPT is specified on the command. [See 5.3.] $PROMPT can be set, using the SET PROMPT command. [See 5.5.]

Any value that $PROMPT is set to is concatenated to the end of the standard protocol message prompt ':', unless the EXACT option is included on the ASK command.

The value of $PROMPT is not altered by an ASK command, such as:

$PROMPT may be up to 32,000 characters long. However, because of an ORVYL limitation, if $PROMPT is more than 160 characters long and an ASK command is issued, an ATTN condition occurs, i.e., any ATTN clause in the command will be executed. Thus, as a prompt, $PROMPT is limited to 160 characters by ORVYL; as a variable in which a value may be temporarily stored, $PROMPT may be as long as 32,000 characters.

6.13.9  ($PROXYID, $PROXYNAME, $PROXYPHONE, $PROXYEMAIL)

The $PROXYID, $PROXYNAME, $PROXYPHONE, and $PROXYEMAIL variables are used by Prism applications that prompt end-users for their University ID and PIN before authorizing them to perform a task. When a user is identified by ID and PIN and also indicates that he is "acting for" someone else, this set of variables contains the ID number, name, phone number, and email address of the person for whom the current user is acting. The current user is acting as the "proxy" for the specified person. [See the "Prism Applications" manual for details.]

6.14.0  ($PRTCNT)

When commands are being issued under Global FOR (or Partial FOR), such as DISPLAY NEXT, TRANSFER, or REMOVE 7, $PRTCNT reflects the number of records to be processed. For example, if the command issued were REMOVE 7, then $PRTCNT would contain the value "7". Here is a chart of special values $PRTCNT can contain; under the heading "Records Requested" are the values given in the command which would result in the $PRTCNT value shown, such as DISPLAY FIRST.

     $PRTCNT           Records Requested
     -------           -----------------
        1              NEXT
        0              *
       -1              FIRST
       -2              LAST
       -3              ALL
       -4              REST

If $PRTCNT is any other positive number, it represents the number of records that were requested to be processed, such as "7" above. Note that $PRTCNT does not reflect how many records were successfully processed; it reflects how many or which records were requested to be processed.

6.14.2  ($PRTLVL)

$PRTLVL is an integer variable that represents the depth to which Partial FOR processing has been nested. If no Partial FOR command is in effect, $PRTLVL is zero.

6.14.6  ($RECSEP)

$RECSEP is an 8-byte character variable containing the string that is used to separate records during output to the active file. The value can be changed to any other string of eight or fewer characters, or even a null string. (Note, however, that the blank line that the string would appear on, as well as the extra semicolon line after each record, will remain.) To make the change use the SET RECORD SEPARATOR command:

The character string given in the SET RECORD SEPARATOR command must be in apostrophes or quotation marks if it contains special characters that are neither letters nor numerals. Any value set for $RECSEP will remain in effect during the entire SPIRES session.

The SET RECORD SEPARATOR command is most useful when large files are being rebuilt and records are output to tape and then added back in to the new file. In that case, if you SET RECORD SEPARATOR "ADD;", then the records going to the tape will be ready to add back to the file in standard SPIRES format.

6.14.8  ($RELPOS)

$RELPOS is an integer variable that represents the relative position of the result or stack pointer. The $RELPOS variable is set to 0 whenever a result or stack is changed or when a new subfile is selected. It is set to 0 when a FOR RESULT, FOR STACK, or FOR SET command is issued for the first time, but remains unchanged if you switch FOR commands within the same session. Each time a record is processed, the $RELPOS variable is incremented by one. It is primarily used to control the relative position of the pointer when records are being sent across the HLI path.

6.15.0  ($RESCNT)

$RESCNT is an integer variable that represents the actual number of records in the search result when a qualifier is being used in a search. $RESCNT is meaningless unless a search using qualifiers is being done. Occasionally the same record may be counted twice in the search result ($RESULT) when qualifiers are used. $RESCNT contains the actual number of unique records in the search and is computed each time it is used. The command SHOW RESULT COUNT displays the value of this variable. [See "SPIRES Searching and Updating, section B.3.5.9, "Index Qualifiers".]

6.15.1  ($RESHIST)

When the user has SET RESULT HISTORY in effect, the $RESHIST variable contains the number of result histories possible, as controlled by the SET RESULT HISTORY command. The default value is 100, but it can range from 20 to 2000. If no result history is set, then the value of this variable is -1.

6.15.2  ($RESNAME)

After a search request, the $RESNAME variable contains the name of the goal record in the currently selected subfile. It is a character variable with a length of 16. A SELECT command sets $RESNAME equal to 'RECORD'; this value does not change until a search command is issued.

For example:

Though the EXTERNAL-NAME statement in the file definition may name both a singular and plural name to appear in search result messages, only the singular form appears in $RESNAME.

6.15.3  ($RESNUM)

When the user has SET RESULT HISTORY in effect, the $RESNUM variable contains the number of the history item associated with the current search result. The history is controlled by the SET RESULT HISTORY command. The value is 0 if the history has just been turned on, or if there is no current result. If result history is not set, then the value of this variable is -1.

6.15.4  ($RESULT)

$RESULT is an integer variable that represents the number of records in the current result. A result is created by the FIND command. The $RESULT variable is not directly setable. If the search uses qualifiers, $RESULT may not give an accurate count; in this case, use the $RESCNT variable.

6.15.6  ($RETCODE (not applicable to Stanford environment))

$RETCODE is an integer variable that is meaningful only at those sites using CMS/SPIRES. Its purpose is to communicate information back and forth between SPIRES and CMS, concerning the success or failure of issued commands.

6.15.7  ($SEARCHMOD)

$SEARCHMOD ($SRCHMOD) is a string variable containing the current search modifier, as set by the SET SEARCH MODIFIER command or by the SEARCH-MOD statement in the Subfile section of the file definition. The variable is null if there is no search modifier in effect or if no subfile is selected.

6.15.8  ($SEED)

$SEED is an integer variable used by the $RANDOM function. [See 7.2.] It is derived from the internal clock, but can be set with the SET SEED command.

6.16.0  ($SELCHR, $DISCHR, and $MINCHR)

$SELCHR, $DISCHR, and $MINCHR are integer variables that contain the values (in cents) of the charging parameters for a subfile. The values are computed from the charging statements in the file definition of the selected subfile.

The following table shows the correspondence:

     $variable                  File Definition Statement
     (in cents)                   (in dollars and cents)
     ----------                 -------------------------

     $SELCHR                    SELECT-CHARGE
     $DISCHR                    DISPLAY-CHARGE
     $MINCHR                    MINIMUM-CHARGE

These variables do NOT contain the accumulated charges as a subfile is used. They are set by SPIRES when a chargeable data base is selected.

6.16.2  ($SELECT ($SEL))

$SELECT (alias $SEL) is a string variable that contains the name of the currently selected subfile. Note that $SELECTED is a flag variable, but $SELECT is a string variable. Neither of them can be SET by the user, except by selecting a subfile.

6.16.4  ($SELECTED)

The $SELECTED flag is TRUE if a subfile is selected. Note that $SELECTED is a flag variable and $SELECT is a string variable that returns the name of the selected subfile. You can only set these variables by selecting a subfile.

6.16.6  ($SEP)

$SEP is a 2-byte character that represents the characters used to separate commands in a one-line protocol. [See 5.7a.] The default is "//".

6.16.8  ($SERX and $SERI)

$SERX and $SERI are integer variables that control the presentation of SPIRES serious error messages.

They are set by the command:

where "number" is 0, 1, 2, 3, or 4 as described for the SET MESSAGE command. [See 5.9.]

$SERX is set by protocol commands, the 'X' suffix denoting protocol XEQ, or with the WITHIN XEQ prefix. $SERI is set by user-issued commands, the 'I' suffix denoting 'interactive', or with the WITHIN INTERACTIVE prefix.

6.17.0  ($SETFORMAT)

$SETFORMAT is a string variable that contains the name of the format currently set. This is the name that you typed with the SET FORMAT command to set the format.

For example,

6.17.1  ($SITE)

$SITE is a three-character variable that names the operating system at the current site. ($SITE remains constant for any given site.) This variable should mainly be useful to system programmers who need to test or modify site-dependent code.

Possible values for $SITE are: STS, CMS, MTS, and TSO. (STS stands for Stanford and the other ORVYL sites.)

6.17.2  ($SLOT/$NOTSLOT)

$SLOT and $NOTSLOT are flag variables that can be used to determine whether or not a subfile's goal record (or subgoal record) is a slot key record. If $SLOT is TRUE, the goal record is a slot key record; if $NOTSLOT is TRUE, the goal record is not. Naturally, a subfile must have been selected before testing these variables; if no subfile is selected, the value of this flag is unpredictable.

6.17.3  ($SORTCODE)

$SORTCODE contains the status code for the SPISORT command. If $SORTCODE is null, then SPISORT succeeded. If an error has occurred, $SORTCODE will "Un" where "n" is some non-zero value, such as "U119". Note that when a SPISORT command fails, $NO is also true. [See Chapter 1 in "SPIRES Technical Notes" for an explanation of the SPISORT command.]

6.17.4  ($STACK)

$STACK is an integer variable that represents the number of records in the current stack. A stack is created by commands such as STACK, EXTRACT, and SEQUENCE. The variable $STACK is not directly setable.

6.17.6  ($STOP/$NOSTOP)

If $STOP is TRUE, then when a command fails either due to incorrect syntax or invalid user input, an execution break occurs, which causes the protocol to stop executing and prompts the user "Continue XEQ?", to which the user must respond YES or NO. If $NOSTOP is TRUE, then a command failure does not cause a break condition to occur, allowing you to test for command failure with the $NO variable and take appropriate action from the protocol.

The SET STOP and SET NOSTOP commands are used to set these flags. The SHOW SETTINGS command shows which of the pair is currently set. The default is STOP. [See 5.10 for more information about using these variables.]

6.17.8  ($SUBCODE)

$SUBCODE is a string variable that is set when a user selects a subfile that has a SUBCODE statement in the file definition. It provides a code up to 32 characters long that can provide additional descriptive identification for the subfile (other than the subfile name) that can be tested in protocols or Userprocs.

6.18.0  ($SUBFSIZE)

$SUBFSIZE is an integer variable that contains the size (number of records) of the currently selected subfile, the same number displayed by the SHOW SUBFILE SIZE command. The value is -1 if no subfile is selected, and -2 if the subfile size cannot be determined because the file has more than 32 record types.

Note that $SUBFSIZE reflects the number of records in the tree, and thus does not reflect the number of added or removed records in the deferred queue. Also, if a user-caused error occurred or the system ever crashed during a SPIBILD run for the file, the value of $SUBFSIZE may be off by a couple of records. This problem can be corrected with the STATUS ALL command.

6.18.2  ($SUBLATCH)

$SUBLATCH is a string variable that contains the value specified in a SET SUBLATCH command. [See 5.26.] It can be up to 32,767 characters long. By default it is null. It can only be set once each time a subfile is selected, and cannot be reset without reselecting the subfile.

This variable can be used to pass a string of information to your application. Its most obvious use is to generate security locks on information in the subfile since it can be set with a SELECT-COMMAND and cannot be reset by the user.

6.18.4  ($SUPERMAX and $SUPERVAL)

$SUPERMAX and $SUPERVAL are integer variables that tell you how many bytes of internal space are available for record-building, as follows:

   $SUPERMAX             - tells you the amount of space available
                           to hold element descriptor information.

   $SUPERVAL             - tells you the amount of space available
                           to hold element values.

   $SUPERMAX+$SUPERVAL   - tells you the total amount of space
                           available for record-building.

The default value for each variable on entry to SPIRES is 32K (or 32,768 bytes). The maximum for each variable is 128K (131072 bytes).

The SET SUPERMAX command actually sets the size of both the areas of internal memory. When you SET SUPERMAX 40K, for example, SPIRES allows up to 80K of memory to be used, 40K for $SUPERMAX's area and 40K for $SUPERVAL's. The fact that SET SUPERMAX is actually allowing for twice as much memory as the declared value is worth remembering if you need to be conscious of region size (e.g., for batch SPIRES).

You can control their values separately by using the SET SUPERMAX command and the SET SUPERVAL command, respectively. For instance, if your record(s) contained a large number of element occurrences but relatively small data values, you might set $SUPERMAX to a high value and $SUPERVAL to a low one. Or if the record contained extremely long element values but had relatively few element occurrences, you might want $SUPERVAL to be set higher than $SUPERMAX. The maximum for each variable is 128K (131072 bytes) (256K maximum total).

Always set $SUPERMAX before $SUPERVAL

If you issue both commands, the order in which you issue them is important, because setting $SUPERMAX always resets the value of $SUPERVAL as well. So when you want the two variables to have different values, set $SUPERMAX first, then set $SUPERVAL.

Note that you must SET these variables as a multiple of "k" (where "k" = 1,024 bytes), but the value returned by a SHOW EVAL command will be in integer form:

6.18.5  ($SYSTYPE)

$SYSTYPE is an integer variable that indicates what SPIRES program you are currently in. The three values this variable can have are:

6.18.6  ($TCOMMENT)

$TCOMMENT is a string variable that is stored with transactions data and can be found in the $TRANSACTIONS record for the given transaction as the element TCOMMENT. It is set with the SET TCOMMENT command (or Uproc in formats).

$TCOMMENT is cleared after each subfile transaction and when a CLEAR SELECT command is issued for the primary subfile.

[See the manual "File Definition" for more information on transaction records and transaction data.]

6.18.8  ($TERMINAL ($TERM, $TER))

$TERMINAL (alias $TERM or $TER) is a character variable that contains the first four characters of the ID of the logged-on terminal.

6.19.2  ($TERMTYPE ($TTYPE))

$TERMTYPE (alias $TTYPE) is a character variable that represents the terminal type that you gave to the system with the SET TERMINAL command. It can be used, e.g., to determine whether a terminal is supported for use with a full-screen application. (DESCRIBE TERMINAL TYPE will give you more information about the different terminal types.) See also the $TERMPAD variable, described in the "Device Services" manual.

6.19.4  ($TIME and $UTIME)

$TIME is a character variable that contains the current time; it returns an 8-byte value of the form HH:MM:SS. It may not be altered by a SET command.

The $UTIME variable represents the current time in unconverted form (four-byte hex). In formats, an OUTPROC action (A73 or $TIME.OUT) may be coded to output the value properly.

The $UTIME variable contains the number of timer increments since midnight in hexadecimal form; each timer increment is two seconds long.

The expression

will return the integer number of seconds since midnight in #TIME.

6.19.6  ($TIMELIMIT)

The $TIMELIMIT variable is an integer variable of length 2. Its value reflects the duration of a time limit established for a command prompt; and if a time limit has been specified for a command prompt, $TIMELIMIT tells whether the timer associated with that time limit has "fired" (that is, whether or not the user responded to the command prompt within the time limit). [See 5.20a for information on the WITH TIMELIMIT prefix, which sets the timer.]

At the successful completion of any SPIRES command, and during the processing of commands that are not prefixed by WITH TIMELIMIT, the value of $TIMELIMIT is 0. Using the WITH TIMELIMIT n prefix resets $TIMELIMIT to the value of "n". If the command is completed within the time limit specified, $TIMELIMIT goes to zero again, but if the user fails to act before the time limit, $TIMELIMIT takes on a negative value, which your protocol can read.

Note that even if a user fails to complete a command within the time limit, $TIMELIMIT will still be reset to either zero or "n" by the time of the next prompted SPIRES command -- you must query its value before that prompt occurs.

6.19.8  ($TIMER)

$TIMER is an integer variable that contains the timer setting from a SET TIMER command (or the default timer setting). This value is used as an elapsed CPU-timer during index and Global FOR searches. The user is given a warning message every time the timer's interval passes.

If the value of the variable is 0, then the timer is not active, and no warning messages are issued.

6.20.2  ($TRACE/$NOTRACE)

If $TRACE is TRUE, the system will log all commands issued by the user, and all system responses, exclusive of data. If $NOTRACE is TRUE, command logging is suppressed. This logging provides the Data Resources Group with information that may help trace system problems, and is completely independent from any logging established by the file owner.

The SET TRACE and SET NOTRACE commands are used to set these flags. The SHOW SETTINGS command shows which of the pair is currently set. The default is TRACE.

Note: Command tracing is currently disabled at most, if not all, SPIRES sites. Contact your SPIRES consultant to find out whether it is enabled at your site.

6.20.4  ($TRANSFER ($TRA))

$TRANSFER (alias $TRA) is a string variable representing the internal form of the key of the record last accessed with either the TRANSFER command or the REFERENCE command.

Note that if any subfile you are working with on any path has secure-switch 10 set (which causes record locking), the value of $TRANSFER will be cleared when you modify ANY record from that subfile other than the one you accessed with TRANSFER. In other words, you should not TRANSFER (or REFERENCE) a record, process another record from a subfile with record-locking, and then expect an UPDATE or MERGE command to process the record you had originally transferred.

A sequence of commands such as the following would fail, and probably result in an S250 error:

[See the manual "SPIRES File Definition" for details on record-locking.]

Note that because the $TRANSFER variable contains the key in its internal form, it is generally not as useful as $KEY.

6.20.6  ($TRUE/$FALSE)

The $TRUE and $FALSE variables are assigned as values of user-defined flag variables and are used to test the condition of the variables. They cannot be set directly. For example, if you had a flag variable named REMOVED, you could issue the following commands:

These are equivalent to:

6.20.7  ($UNIVID, $UNIVNAME, $UNIVPHONE, $UNIVEMAIL)

The $UNIVID, $UNIVNAME, $UNIVPHONE, and $UNIVEMAIL variables are used by Prism applications that prompt end-users for their University ID and PIN before authorizing them to perform a task. When a user is identified by ID and PIN, this set of variables contains the ID number, name, phone number, and email address for the current user, as stored in the University ID file. [See the "Prism Applications" manual for details.]

6.20.8  ($UPDTYPE)

$UPDTYPE is an integer variable that contains a number indicating the type of update operation that is pending. Its value is set under the following conditions:

    $UPDTYPE           Update Condition
    --------           --------------------------------------------
      0                no update pending
      1                an ADD has just been completed
      2                normal UDPATE (complete record replacement)
      3                TRANSFER/UPDATE requiring merging with
                       non-updatable elements
      4                MERGE

A TRANSFER command will set the value to non-zero according to the above chart, and the subsequent UPDATE command will normally set it back to zero.

6.21.2  ($UPPER/$UPLOW)

If $UPPER is TRUE, the system automatically translates all data received from the terminal to uppercase before it is given to SPIRES. If $UPLOW is TRUE, no translation takes place and the data is accepted the way it is entered.

The SET UPPER and SET UPLOW commands are used to set these flags. The SHOW SETTINGS command shows which of the pair is currently set. The default is UPLOW.

The UPPER and UPLOW options of the ASK command are independent of this variable setting. The following commands would accept the user's response, converting it to upper case in the first example and accepting the value as typed in the second example, regardless of the setting of the $UPPER/$UPLOW flags.

Note that the SET UPPER and SET UPLOW commands have a different meaning in Wylbur. If you have SET WYLBUR, you must precede these commands with a "/" to set this flag in SPIRES.

6.21.4  ($USER ($USE) and $GCODE ($GRO))

$USER (alias $USE) and $GCODE (alias $GRO) are character variables that contain the values of the user code ($USER) and the group code ($GCODE) of the logged-on account ($ACCOUNT). For example, if $ACCOUNT were AB.ELA, then $USER would be ELA and $GCODE would be AB (the account's group prefix).

6.21.6  ($UCODE ($USERCODE, $UCD))

$UCODE (alias $USERCODE or $UCD) is a string variable that has the value of the most recently encountered A56 or SET UCODE string given during a processing rule string. The value of $UCODE is set to null when the next processing rule string begins execution.

6.21.8  ($USERNAME variable)

$USERNAME is a string variable that represents the "user name" that you gave to the system with the SET NAME or SET LINE command. (HELP SET NAME or HELP SET LINE will give you more information about these commands).

6.22.2  ($VERSION)

$VERSION is a character variable 24-characters long that represents the version of the SPIRES system being used. It is in the form:

where "date" is in the form "yy.mm.dd". In the online system, "account" is either:

In batch SPIRES, "account" is always "SPIRES", so there is no distinction between versions. (If you call SPIRES from a BATWYL or BATSPI job, the "online" distinction described above does exist.)

6.22.4  ($WARNX and $WARNI)

$WARNX and $WARNI are integer variables that control the presentation of SPIRES warning messages. They are set by a command of the form:

where "number" may be 0, 1, 2, 3, or 4 as described for the SET MESSAGE command. [See 5.9.]

$WARNX is set by protocol commands, the 'X' denoting protocol XEQ, or with the WITHIN XEQ prefix. $WARNI is set by user-issued commands, the 'I' denoting 'interactive', or with the WITHIN INTERACTIVE command.

6.22.6  ($WDSL ($WDSLINE))

$WDSL (alias $WDSLINE) is a line variable that contains the line number of the last line read from the active file by a SPIRES command other than WDSR. [See 5.11.] For example, after an ADD command that reads data from the active file, $WDSL would contain the last line number read:

If an input format is in effect and the frame dimensions are either "0,0" or "1,n", indicating line-by-line processing, then $WDSL represents the last line read by GETDATA.

6.22.8  ($WDSR)

$WDSR is a line variable that returns the line number of the line just read from the active file by a WDSR command. You may not set this variable, except by executing a WDSR command. (When you issue the SET WDSR command, you in fact set the $NEXTWDSR variable, not the $WDSR variable.)

6.23.2  ($WDST)

$WDST is a line variable that can serve as temporary storage for line numbers. It may be set with the SET WDST command, [See 5.13.] and its value does not affect active file reading or writing. Issuing a SET WDST command when there is no active file sets $WDST to -1. You can use this variable in conjunction with the SET WDST command to find out whether there is anything in your active file as follows:

6.23.4  ($WDSW)

$WDSW is a line variable that returns the line number of the line just written to the active file. You may not set this variable, except by executing a WDSW command. (In fact, as with the $WDSR variable, when you SET WDSW you are really setting the $NEXTWDSW variable.)

6.23.6  ($WIDTH ($WID))

$WIDTH (alias $WID) is an integer variable that holds the current width setting. It is used by Device Services when establishing a default width for an area; it is also used when determining the width of the terminal display when messages are set to level 4 and for some SHOW commands.

At the start of a SPIRES session, the width is 80, but this can be changed with the SET WIDTH command. [See 5.22.] This is independent of the MILTEN width setting.

If you have SET WYLBUR or used the "(W)" parameter on the CALL SPIRES command, you must precede the SET WIDTH command with the "/" for it to have effect in SPIRES.

6.23.8  ($XEQ)

$XEQ is a string variable that returns the name of the currently set XEQ subfile. This may be different from the currently selected subfile.

6.23.9  ($XEQLVL)

The integer variable $XEQLVL tells the level at which an executing protocol is currently nested. (This is equivalent to the number of RETURN commands needed to return from nesting to command level.) You can use this variable, along with such tools as the SHOW XEQ STACK command or the $XEQSTACK function, to help keep track of how deeply your protocol has nested.

To see how the variable works, consider a protocol with the following nested structure:

  Sample Protocol         $XEQLVL
  ---------------------   -------
  -> ..test.protocol         0      <--at command level

  * TEST.PROTOCOL            1      <--any executing protocol starts
      :                                off nested one level
  Xeq Proc NESTED.PROC
      :
  ++NESTED.PROC              2      <--this PROC nests a level deeper
      :
  Xeq Proc DEEPEST.PROC
      :
  ++DEEPEST.PROC             3      <--this PROC nests one more level
      :

Thus, for example, since the PROC DEEPEST.PROC is 3 levels deep, it takes three iterations of the RETURN command to return directly to command level from DeepestProc.

Note that $XEQLVL does not count BREAK XEQ levels as additional levels of nesting.

The maximum number of nesting levels is 100.

6.24.1  ($XTRACE)

$XTRACE is an integer variable that can be tested to see whether protocol tracing (via the SET XTRACE command) is in effect. If the value is non-zero, some form of tracing is activated.

Specifically, SPIRES sets the value of $XTRACE through bit manipulation, depending on particular trace settings. In effect, SPIRES sets the value by adding up various numbers according to the following table:

So, if the commands SET XTRACE, SET XTRACE JUMP and SET XTRACE VARIABLES had been issued in succession, the value of $XTRACE would be 13.

Note that if tracing has been temporarily suppressed because the currently executing protocol isn't meant to be traced (due to the SET XTRACE PROTOCOLS command), then the value of $XTRACE will be 128.

6.24.2  ($YES/$NO)

The $YES and $NO flag-type variables reflect whether the previous command executed correctly or failed to execute properly. If $YES is TRUE, the previous command succeeded. If $NO is TRUE, the previous command failed.

  -> select formats
  -> show eval $no
  0
  -> select fromats      <--command will "fail" because of spelling
  -> show eval $no
  1

You cannot set either of these two variables (except by executing a command).

Testing Commands With $NO

In addition to testing SPIRES commands, $NO can be used to test Wylbur commands such as the PRINT command. It is a good idea to use $NO to test the critical commands in your application -- i.e., commands such as SELECT or SET FORMAT, whose failure would cause later commands either to fail or to become meaningless:

  ++Select
  /Select #SubFileName
  If $No Then Xeq Proc Select.Error
     :

If you test $NO, be sure to test it immediately after the command in question, since every succeeding command (except a command prefixed by "-") will reset the variable's value.

Command Testing vs. Condition Testing

It's important to keep in mind the difference between condition-testing and command-testing. To test whether a condition is true or false, you use block structures or a combination of IF, THEN and ELSE -- by contrast, $YES/$NO merely test whether the preceding command executed without error. [See 3.1, 5.8.]

In the example below, $NO tests whether a command (in this case, TRANSFER) successfully executes. $NO would be set if, for instance, no record had a key matching the value of the #USERKEY variable. By contrast, the IF... THEN statements test an entire condition. In the first case, IF... THEN tests whether or not #USERKEY can be successfully converted to an integer, while, in the second case, it tests whether $NO has been set to "true":

  If $TypeTest(#UserKey,INT) ~= 'INT' Then Xeq Proc Bad.Value
  /Transfer #UserKey
  If $No Then Xeq Proc Transfer.Error
    :

$NO always asks, "Did the last command succeed?" The IF-test always asks, "Did the stated condition evaluate to true or to false?" Note that $NO doesn't tell you why a command failed, so you may need to investigate further into an error's specific cause.

6.24.4  ($ZRESULT/$NZRESULT ($ZRES/$NZRES))

The $ZRESULT and $NZRESULT flag variables (alias $ZRES and $NZRES) allow you to determine whether the last search command resulted in a zero result. If a search command has a zero result, then $ZRESULT is TRUE; $NZRESULT is TRUE if the search command retrieves at least one record. The $ZRESULT variable is also TRUE if a search command temporarily caused a zero-result condition. Remember that SPIRES will automatically backup to a non-zero result if a search command would reduce the result to zero.

For example:

$ZRESULT will be TRUE and $NZRESULT will be FALSE after the last command above is issued.

You can also test the value of the $RESULT variable to see if a search result exists, and the number of records in it.

7  Functions

7.1  Overview

A SPIRES system function is a processing operation, predefined and built into SPIRES, which you can use to test, convert, or manipulate an input value that you provide as part of the function's "argument-list". The "argument-list" is a list of one or more values and parameters that appears in parentheses after the function-name itself.

A given function returns a single output value (the result of processing the input value in its argument-list), and this value is often either assigned to a variable with the LET statement, or tested in the IF statement of an IF...THEN command, as in these examples:

   LET MONTH = $LEFTSTR($DATE,2)

      (The function $LEFTSTR returns the input value $DATE,
      converted to a string, and shortened to its first
      2 characters, which LET assigns to the variable #MONTH.)

   IF $RECTEST(#value) = 0 THEN SHOW EVAL "Record does not exist"

      (The function $RECTEST tests whether a record with key
      equal to #value already exists in the selected subfile
      and, if not, displays the message shown)

In an argument list the first item listed usually represents the input value upon which the function operates, and other arguments, if any are listed, usually specify how the function should process the input value. Thus for $LEFTSTR($DATE,2) the input value to be processed is the current value of the system variable $DATE, and the value 2 indicates how the input value is to be truncated to create a new value.

Note that a function, unlike a system proc, does not actually change its input value, but instead returns a new value based on processing of the input value. For example, $LEFTSTR above does not change the value of $DATE, but returns a new value based on $DATE. [Each of these functions is described fully in the alphabetical section at the end of this chapter.]

In the statement below, if #X is a string to begin with, the $INTEGER function will not change it to an integer, but instead will return a new value, converted to integer form (if possible), and will place this converted value in the user variable NUM:

General Syntax

The general syntax for functions is as follows:

"Argument-list" consists of one or more strings or variable names separated by commas. Arguments with embedded blanks, commas, or most other non-alphanumeric characters must be enclosed in delimiter characters (quotation marks or apostrophes), and if the character delimiting the argument (e.g., an apostrophe) is itself used in the argument, it must be doubled. [The punctuation characters that do not have to be enclosed in delimiter characters are the following twelve: .!%^`_{}[]\?]

To indicate explicitly that you want an argument to be null, you can use two apostrophes (''), as in $CHANGE(colour,u,''). To use an argument's default-value, when there is one, you can leave its position empty, as in $GETUVAL(name,,'None'). If the argument does not have a default value, leaving the position empty again causes the argument to be null: $CHANGE(colour,u,).

Data Type of Arguments

In general, each argument in a function's argument-list must be (or be convertible to) a particular "data type", such as string or integer, for the function to succeed -- e.g., for $LEFTSTR the first argument must be convertible to a string and the second must be convertible to an integer. When an argument is not already of the necessary data type, SPIRES will convert it to that type before processing the function, but if the conversion is impossible, the function will fail and you will receive a "conversion error". [See 5.1.2.] For instance, a conversion error occurs below because 0.5 can not be converted to an integer:

    -> let IntNum = $integer(0.5)
    -Conversion error:  STR to INT , value = '0.5'
    -Not a legal or complete command

The "variable data type conversion" functions such as $REAL or $PACKED [See 7.1.1.] can help ensure that an argument or value will be of a particular data type.

Expressions in Functions

The argument of a function can be an expression as well as a simple variable or string. [See 5.1.1.] (Expressions include arithmetical computations, concatenated strings, and functions themselves.) This can be useful in the IF...THEN command, [See 5.8.] where a function can be used to "smuggle" in other kinds of expressions:

     -> if 2 + 2 = 4 then show eval 'You finally got it.'
     -Unrecognized: + 2 = 4 then ...
     -> if $int(2 + 2) = 4 then show eval 'You finally got it.'
     You finally got it.

As the above paragraph suggests, the argument of a function can be another function, in which case the functions are processed from the inside out:


Functions and Tasks

For certain functions, the value that the function returns may be incidental to the task that you wish the function to perform. One example of this is the $DYNASET function, which sets a value into a dynamic array and returns a count of the array members whose value was set. The count that the function returns is probably incidental to the task of setting the array in the first place.

This type of function is often called into action with the EVAL command, e.g.:

    EVAL $DYNASET(array1,0,1,10).

     (The command above would set the first through tenth member of
     dynamic array ARRAY1 to zero.)

Categories of Functions

Functions can be categorized, somewhat loosely, according to the kind of task that they accomplish:

Variable Data Type Conversion

    $CHAR ($CHARACTER, $TRIM)            $NEGATIVE  ($NEG)
    $FLAG                                $PACKED    ($PACK)
    $HEX                                 $REAL
    $INTEGER  ($INT)                     $STRING    ($STR)
    $LINE                                $WDS

    $RETYPE                              $TYPE
    $REF                                 $TYPETEST

String Manipulation

    $BREAK                               $REVERSE
    $CAPITALIZE  ($CAP)                  $RIGHTSTR  ($RSTR)
    $CASE                                $RIGHTSUB ($RSUB)
    $CHANGE                              $RMATCH
    $CHAR                                $SIZE
    $COMPARE                             $SPAN
    $DOUBLE                              $SQU
    $ENCIPHER                            $STRIP
    $INSETC, $INSETL, $INSETR            $SUBSTR
    $LEFTSTR  ($LSTR)                    $TRANSLATE
    $LEFTSUB  ($LSUB)                    $TRIM
    $MATCH                               $VERIFY
    $PARSE, $PARSESTRIP                  $XSTR
    $PMATCH                              $XSUB

Variable (and Variable Array) Manipulation

    $AMATCH                              $GETPARMS, $SETPARMS
    $APMATCH                             $REF
    $ARMATCH                             $SET
    $ASET                                $VARPUT, $VARGET
    $ASORT, $ASORTX                      $VARTEST
    $AUTHPARMS                           $VGROUPALTER  ($VALTER)
    $DYNASET, $DYNAZAP                   $VGROUPINIT  ($VINIT)
    $DYNGET, $DYNPUT, $DYNZAP            $ZAP

Numeric and Packed Decimal Manipulation

    $DECIMAL                             $RANDOM
    $EDIT                                $REMAINDER
    $EXP                                 $SQRT
    $LOG                                 $TRUNC
    $MOD                                 $UNEDIT
    $PACKTEST                            $WINDOW
    $PRECISION

Subfile and Data Value Information

    $DEFQTEST                            $PATHFIND, $PATHINFO
    $ELEMINFO, $ELIDINFO                 $PRISMINFO
    $ELEMTEST, $ELIDTEST, $ELNOTEST      $PROCSUBG
    $FRAMETEST                           $RECINFO
    $GETCVAL, $GETUVAL,$GETIVAL,$GETXVAL $RECTEST
    $GETVMATCH                           $RESINFO
    $GETVOCC                             $SEARCHINFO
    $INDEXINFO                           $SEARCHTEST
    $INDEXNUM, $INDEXTERM                $SSW
    $LOOKSUBF,$LOOKSUBG                  $TRANINFO
    $LOOKSYS                             $COLUMNTEST

Date and Time Manipulation

    $DATEIN                              $TIMEOUT
    $DATEOUT                             $WORKDAYS
    $DAYS                                $XDATE
    $TIMEIN                              $YYCALC, $YYTEST

Other Miscellaneous Utilities

    $ACCESSTEST                          $ISSUEMSG
    $CHECK                               $NOLF
    $CLRSUBF                             $SYSEVAL
    $EVALUATE   ($EVAL)                  $SYSTEM
    $GETELOG                             $TEST
    $ISSUECMD   ($ISSUE)                 $XEQSTACK

Functions Associated With Device Services Areas

    $GETAREA                             $SETAREA
    $PUTAREA

    (Note: functions associated with Device Services
     areas are documented in "SPIRES Device Services".)

Functions Associated With Triples

    $ATTRIBUTE    ($ATTR)                $MAKE
    $GROUP        ($GRP)                 $MADE
    $GROUPELEMENT ($GROUPELEM)           $OBJECT
    $GROUPSIZE    ($GRPSIZE, $GRPSZ)     $UNMAKE
    $GROUPSORT    ($GRPSORT)             $UNMAKETRIPLE  ($UNMAKETRI)
    $LOOKUP                              $VALUE

    (Note: for functions with triples see Chapter 7A.)

7.1.1  Functions for Variable Data Type Conversion (Summary)

The "data type conversion" functions take an input value and return a value representing the input value converted to the data type that the function names. For instance, LET Y = $INTEGER(#X) takes the input value #X, converts it to an integer (if possible) and places the new value in the user variable Y.

Type conversion can be important whenever you use mixed types within an expression such as an arithmetical computation. [See 5.1.2.] For instance, if you try to divide an integer value by a real value, without first converting the integer's data type, you will probably receive a conversion error; you can avoid this error by first converting the integer to real (i.e., floating point) with the $REAL function:

    -> let x = $INT(6)
    -> let y = $REAL(1.5)
    -> show eval #x / #y
    -Conversion error, REAL to INT ...
    -> show eval $REAL(#x) / #y
    4

Three other functions also fall loosely into the "data type conversion" category. The $TYPE function returns the current data type of its input value. The $TYPETEST function tests whether a type conversion of its input value would succeed or fail (without actually performing the conversion). The $RETYPE function returns its input value's type redefined to the type named in its argument list, without changing the value.

The conversion functions listed below have single arguments consisting of a string or an expression. Remember that these functions do not redefine the variable specified in the argument, but merely return its current value redefined to the type specified. [The stored type of an element, as determined in a file definition by the TYPE statement, does not necessarily correspond to the types described here. For instance, an element defined in its file definition as TYPE = STR (structure) would contain values of data type HEX. See the "SPIRES File Definition" manual for more on TYPE statements in a file definition. The $ELEMTEST, $ELIDTEST, and $ELNOTEST functions, described in this chapter, let you determine an element's stored type.]


  $STRING ($STR)    returns value converted to string
  $CHARACTER        returns value converted to CHAR
  ($CHAR, $TRIM)       (i.e., fixed-length string)

  $INTEGER ($INT)   returns value converted to type INT
  $NEGATIVE ($NEG)  returns value converted to type INT
                       and with its sign changed
  $ABS              returns value converted to string
                       and with leading signs removed
  $REAL             returns value converted to type REAL
                       (i.e., floating point)
  $PACKED ($PACK)   returns value converted to type PACK
  $HEX              returns value converted to type HEX

  $FLAG             returns value converted to type FLAG

  $LINE             returns value converted to type LINE
  $WDS              returns value converted to type WDS

Three other functions also convert or test a value's data type:


  $RETYPE           returns a value based on the input value but with
                       a redefined data type -- e.g., $RETYPE(E,HEX)
                       returns C5

  $TYPETEST         tests whether value can be converted to a named
                       data type -- e.g., $TYPETEST(#A,INT) tests
                       whether #A can convert to an integer.  (Returns
                       null if the conversion would have failed.)

  $TYPE             returns the current type of its input value, in
                       the form INT, REAL, PACK, HEX, etc.

Because the last two functions named can be used to test variables, they are also included under the category of "variable manipulation" functions. [See 7.1.3.]

7.1.2  String Manipulation Functions (Summary)

"String manipulation" functions generally scan and manipulate (e.g., shorten) an input string that is given as the first argument of the function, often producing a shorter or altered substring from it. (As always, the function does not change the input value in itself, but returns a new value based on processing of the input value.)

For instance, the command below asks $BREAK to scan the input value up to the first colon and break it to the left of that point, placing the new value in the user-defined variable HOUR:

Although most of the string functions listed in this section return a string value, not all of them do so. The two pattern-matching functions, $MATCH and $PMATCH, compare an input value with a list of string patterns, returning an integer value corresponding to the match (if any) that is made:

In addition, the $SIZE function returns an integer corresponding to the length of the string form of its input value. [For the $UNEDIT function, which could also be considered a "string manipulation" function, see section 7.1.4]

Here are short descriptions of the string manipulation functions, grouped in roughly related subcategories:

  --------------
  Capitalization
  --------------

  $CAPITALIZE ($CAP)  returns its input value converted to upper case
                         -- e.g., $CAP(word) returns WORD
  $CASE               returns its input value converted to one of 14
                         different formats using upper and lower case
  -----------
  Translation
  -----------

  $ASCII             change from ASCII to EBCDIC, or visa-versa.
  $CHANGE            changes all occurrences of one specified string
                        in the input value to a different string,
                        e.g., $CHANGE(GG.UUU,'.','') returns GGUUU
  $CHANGELIST        changes all occurrences of multiple specified
                        strings in the input value to different
                        strings; like $CHANGE but with multiple
                        strings to change
  $TRANSLATE         changes all characters that occur in a specified
   ($TRANS)             string to the corresponding characters in
                        another string, e.g., $TRANS(ABCD,ABC,123)
                        returns 123D
  $INSETC,           inlays a string value within a specified field
  $INSETL,              and adjusts it center (left or right) -- e.g.,
  $INSETR               $INSETL('Name',.,10) returns Name......
  $ENCIPHER          returns a random enciphered (hex) value from an
                        input string value
  $DOUBLE            doubles specified character such as quotation
                        marks
  $SQU               removes leading, trailing, and multiple blanks --
                        e.g., $SQU('SQUEEZE   ME') returns SQUEEZE ME
  $SIZE              returns what the length of the input value
                        would be if it were converted to a string --
                        e.g., $SIZE(sample) returns 6
  $REVERSE           returns the input value with its characters in
                        reverse order, e.g., $REVERSE(spires) = serips
  ----------
  Substrings
  ----------

  $LEFTSTR,          returns a substring of the input value truncated
  $RIGHTSTR             to its leftmost (rightmost) portion for a
  ($LSTR, $RSTR)        specified integer number of characters --
                        e.g., $LSTR(FIND,1) returns F

  $LEFTSUB,          returns a substring of input value truncated to
  $RIGHTSUB             the left (right) of a specified substring --
  ($LSUB,$RSUB)         e.g., $LSUB(SELECT,E) returns S

  $SUBSTR            returns a substring of the input value beginning
                        at a specified character (e.g., returns the
                        first 4 characters beginning with 5th
                        character of input value)

  $XSTR              returns substring of the input value truncated
                        either from the front or from the back for
                        a specified number of characters

  $XSUB              returns substring of the input value truncated
                        either from the front or the back using a
                        given substring as the dividing point
  $BREAK             returns substring of input value broken at a
                        point to the left of a character in a second
                        string -- $BREAK('12/10/86','/') returns 12

  $PARSE             operates like $BREAK unless the input value is
                        in a "containing group" such as apostrophes

  $PARSESTRIP        takes the result from $PARSE and strips off
                        containing group characters

  $SPAN              returns substring of the input value broken to
                        the left of any character found in the value
                        and not in a second specified string -- e.g.,
                        $SPAN(BOOK,OEB) returns BOO
  $STRIP             returns substring of the input value beginning
                        with first character found in the value
                        and not in a second specified string -- e.g.,
                        $STRIP(BOOK,OEB) returns K

  $COMPARE           returns the part of an input value that compares
                        with (matches) a second specified string
  ----------------
  Pattern Matching
  ----------------

  $MATCH             compares the input value with a predetermined
                        list of string patterns and returns an integer
                        corresponding to the number of the match,
                        e.g., $MATCH(Z,X,Y,Z) returns 3.

  $PMATCH            acts like $MATCH but is often used for matching
                        against exact stems

  $RMATCH            acts like $MATCH but reverses the pattern and
                        the strings

  $VERIFY            checks that all the characters in the input
                        value are either like or unlike the
                        characters in a second string, e.g.,
                        $VERIFY($ASK,like,ABCDEFGHIJKLMNOPQRSTUVWXYZ')
                        checks that $ASK contains only capital letters

7.1.3  Variable Manipulation Functions (Summary)

The following "variable manipulation" functions establish values in arrays, initialize values, zap dynamic variables, etc. [See 4 for more information on user-defined variables.]

  ----------------------
  Establishing variables
  ----------------------
  $DYNPUT           makes a dynamic variable (often from within
                       a format or Userproc)
  --------------------
  Establishing a value
  --------------------
  $ASET             sets members of a static array to a specific
                       value -- e.g., EVAL $ASET(group,0) sets value
                       of members of the array GROUP to zero
  $AUTHPARMS        passes to Routing parms needed for authority checking
                       (used in Prism routing applications)
  $DYNASET          sets values of members of a dynamic array
  $DYNPUT           assigns value to a dynamic variable (often from
                       within a format or Userproc)
  $GETPARMS         assigns stored values from $SETPARMS to named
                       variables (see section 4.5.1)
  $REF              assigns a static variable to another static
                       variable defined as a reference variable
  $SET              set the value of system variable $ASK, $PROMPT,
                       $PARM or a Prism system variable
  $SETPARMS         saves parameter values internally till
                       retrieved by $GETPARMS (see section 4.5.1)
  $STATPUT          assigns value to a static variable (usually from
                       within a format or Userproc)
  $VARPUT           assigns value to a static or dynamic variable
  ---------------
  Testing a value
  ---------------
  $DYNGET           retrieves a dynamic variable's value, often
                       from within a format or USERPROC
  $STATGET          retrieves a static variable's value, usually
                       from within a format or USERPROC
  $TYPE             returns the current type of its input value,
                       in the form INT, REAL, etc.
  $TYPETEST         tests whether value can be converted to a named
                       data type -- e.g., $TYPETEST(#A,INT) tests
                       whether #A can convert to an integer. (Returns
                       null if the conversion would have failed.)
  $VARGET           retrieves the value of a dynamic or static
                       variable
  $VARTEST          return attribute information about a static
                       variable
  ----------------
  Pattern matching
  ----------------
  $AMATCH           matches input value against the values in
                       an array, and returns an integer
                       corresponding to the number of the match
  $APMATCH          acts like $AMATCH but is useful for matching
                       exact stems (compare $MATCH and $PMATCH)
  $ARMATCH          acts like $MATCH but is useful for stem
                       matching of multiple word entries
  --------------------
  Destroying variables
  --------------------
  $DYNAZAP          destroys one or more members of a dynamic
                       array
  $DYNZAP           destroys a dynamic variable, usually from
                       within a format or USERPROC
  $ZAP              destroys a dynamic variable, usually from within
                       a protocol -- e.g. LET N = $ZAP destroys N
  --------------------
  Manipulating vgroups
  --------------------
  $ASORT ($ASORTX)  sorts the members of an array
  $VGROUPALTER      changes the size and reallocates a variable
  (VALTER)             group
  $VGROUPINIT       initializes the variables of the named vgroup
  ($VINIT)

7.1.4  Functions for Numeric and Packed Decimal Manipulation (Summary)

The following functions perform computations or manipulate numeric or packed decimal values in other ways. Packed decimals in SPIRES are a special form of numeric value useful in arithmetic requiring large ranges or extreme precision. [See the manual "SPIRES Technical Notes" for full information on packed decimals.]

To the following list might be added the $NEGATIVE function described earlier [See 7.1.1.] which changes the sign of its integer-type input value.

  ------------
  Computations
  ------------
  $SQRT            takes the square root of an input value of
                      type real -- e.g., $SQRT(9) returns 3
                      (and this returned value is type real)

  $EXP             raises a packed decimal input value to the power
                      specified by the 2nd (integer) argument --
                      e.g., $EXP(10,2) returns 100 (returned
                      value is packed decimal)

  $ROOT            takes the root of a packed decimal input value
                      according to the 2nd (integer) argument --
                      e.g., $ROOT(1000,3) retuns 10 (returned
                      value is a string suitable for $PACK)
  $LOG             takes the logarithm of an input value of
                      type real (returned value is type real)

  $MOD             returns remainder of integer divided by 2nd
                      integer -- e.g., $MOD(10,3) returns 1
                      (returned value is type integer)

  $RANDOM          given an integer input value, generates a
                      random integer value between 0 and the
                      input value -- e.g., $RANDOM(90) might
                      return a value of 83 or 26 or...

  $REMAINDER       given 2 packed decimal input values, returns
                      the remainder, in packed decimal form, of
                      their division
  ----------
  Truncation
  ----------
  $TRUNC           truncates a real value to its integer portion
                      -- e.g., $TRUNC(1.23) returns 1
  ----------
  Edit Masks
  ----------
  $EDIT            (re)formats a value according to an edit mask
                      -- e.g., $EDIT(1,'$$$.$$') returns the
                      string $1.00

  $UNEDIT          strips off edit mask editing, returning a
                      string convertible to a packed decimal --
                      e.g., $UNEDIT('$1.00CR') returns -1.00
  ------------------------------
  Other Packed Decimal Functions
  ------------------------------
  $DECIMAL         creates packed decimal value with the specified
                      number of decimal places -- e.g.,$DECIMAL(3,2)
                      returns 3.00

  $PRECISION       changes the precision of a packed decimal value --
                      e.g., $PRECISION(0.236,2) returns 0.24
  $WINDOW          similar to $PRECISION but does not add precision

  $PACKTEST        tests the magnitude, etc., of a packed value

  $NORMALIZE       normalizes a packed decimal input value into a
                      standard form, stripping insignificant zeros
                      (returned value is a string suitable for $PACK)

7.1.5  Functions for Subfile and Data Value Information (Summary)

The following functions retrieve information from the selected subfile or (in the case of $LOOKSUBF and $LOOKSUBG) look up information in a separate record-type.

  --------------
  Deferred Queue
  --------------
  $DEFQTEST         retrieves information about the deferred queue
                       of the selected subfile, e.g., whether it
                       contains records or when it was last processed
  -----------
  Record Test
  -----------
  $RECINFO          returns information about a given record in the
                       selected subfile, such as its storage size

  $RECTEST          tests whether input value is the key of an
                       existing record and, if so, tells whether the
                       record is newly-added, updated, etc.
  --------
  Security
  --------
  $SSW              tests whether the named secure-switch is set, e.g.,
                       $SSW(13) tests secure-switch 13
  ------------------------
  Elements and Searchterms
  ------------------------
  $ELEMINFO,        retrieves element information for the
  $ELIDINFO            currently selected file

  $INDEXINFO        retrieves index information for the
                       currently selected file

  $ELEMTEST,        retrieves information about the named element,
  $ELIDTEST,           such as its position in the file definition
  $ELNOTEST            or the security defined for it

  $INDEXNUM         retrieves the number of the named searchterm's
                       ordinal position in the SHOW INDEXES list
  $INDEXTERM        retrieves index information (type, searchterms)
                       for a given index

  $COLUMNTEST       retrieves the name of an element whose ELEMINFO
                       packet contains a given RDBMS_COLUMN name.
  -------------
  Frame Testing
  -------------
  $FRAMETEST        retrieves information about frames in the
                       currently set format
  ----------------------
  Retrieving Occurrences
  ----------------------
  $GETCVAL          retrieves an element occurrence in external form
                       from a referenced record
  $GETIVAL          retrieves an element occurrence in internal form
                       from a referenced record
  $GETUVAL          retrieves an element occurrence in internal form
                       from a referenced record
  $GETXVAL          retrieves an element occurrence in external form
                       from a referenced record
  $GETVMATCH        retrieves the number of an element occurrence
                       that matches a given string
  $GETVOCC          retrieves the number of occurrences of an
                       element
  ------------------------------------------------
  Looking Up Values in Another File or Record-Type
  ------------------------------------------------
  $LOOKSUBF         accesses a record from another subfile, e.g., to
                       verify or replace information
  $LOOKSUBG         accesses a record from another record-type, e.g.,
                       to verify or replace information
  ----------------------------------------
  Running a Value Through Processing Rules
  ----------------------------------------
  $PROCSUBG         processes its input value through the Inprocs
                       (or Outprocs) defined for a specific element
                       in the selected subfile's goal or subgoal
                       record-types

  $SEARCHTEST       verify that the input value is a valid search
                       command, passing any search values through
                       the appropriate Searchprocs
  ------------------
  Search Information
  ------------------
  $PRISMINFO        returns information about the Prism user's
                       current search (among other things)

  $RESINFO          returns information related to result history
                       entries

  $SEARCHINFO       retrieves commands issued for the current
                       search -- e.g., $SEARCHINFO(1,com)
                       returns the first search command issued
  -----------------------------
  Transaction Group Information
  -----------------------------
  $TRANINFO         returns information about the status of the
                       currently open transaction group, if any
  -----------------
  Path Information
  -----------------
  $PATHFIND         retrieves path-number of currently opened
                       path, based on a path-string and code --
                       e.g., $PATHFIND(books,SUBFILE) is 1 if the
                       BOOKS subfile is selected on path number 1
  $PATHINFO         retrieves information about an open path,
                       specified by number -- e.g., $PATHINFO(1,TYPE)
                       returns "Format" if a format is set on path
                       number 1

7.1.6  Functions for Date and Time Manipulation (Summary)

The following functions reformat date or time values to prepare them for storage or display, or to make computations with them easier:

  -----------------
  Date Reformatting
  -----------------
  $DATEIN           verifies that input value is a valid date
                       and converts into internal (hex) form
  $DATEOUT          takes date in hex form and returns it
                       converted into one of several external
                       formats
  $DAYS             takes date value in hex form and converts it
                       to the number of days since Jan 1, 0000
                       (for computations with $XDATE)
  $XDATE            converts a date in $DAYS form back into
                       hex form
  -----------------
  Date Processing
  -----------------
  $WORKDAYS         finds the date "x" workdays from the given date
  $YYCALC           does arithmetic and does conversions involving
                     two-character years (e.g., 99=1999, 00=2000)
  $YYTEST           compares two two-character year values
  -----------------
  Time Reformatting
  -----------------
  $TIMEIN           converts a time string into internal millisecond
                       units, for storage or computation
  $TIMEOUT          converts a time string, usually in milliseconds,
                       into one of several different external formats
  -------------------
  DateTime Processing
  -------------------
  $DATETIME         convert combination of date and time.

7.1.7  Other Miscellaneous Functions (Summary)

The following functions don't fall into any of the previous categories, but are useful for miscellaneous tasks:

  $ACCESSTEST      determines whether the current user is listed
                      in access lists of an EXTDEF record
  $BNF             parse input string into tokens
  $CHECK           returns the check digit that would be applied
                      to the specified integer input value,
                      according to the code you specify
  $CLRSUBF         clears the named subfile as a subgoal, and
                      detaches the file
  $EVALUATE        evaluates expressions, usually from within a
                      format or USERPROC (compare the slash prefix
                      in the protocols language)
  $GETELOG         retrieves data from the error log
  $ISSUECMD        issues a non-SPIRES command, usually from within
                      a format or USERPROC -- e.g.,
                      EVAL $ISSUECMD(command)
  $ISSUEMSG        issues a "system message" of your design
  $NOLF            writes a string to the terminal, suppressing the
                      final line feed
  $SYSEVAL         passes an expression to WYLBUR for evaluation

  $SYSTEM          returns fully qualified name of system files and
                      formats.  Can also be used to validate accounts
  $TEST            tests whether a condition is true or false
                      (similar to the IF condition in an IF...
                      THEN command)
  $XEQSTACK        tracks nesting of execution levels in protocols
                      (compare the SHOW XEQ STACK command)

7.2  Function Descriptions in Alphabetical Order

7.2.0b  (The $ABS Function)

--------------------------------------------------
Function: $ABS
Purpose: Return the absolute value of the input
--------------------------------------------------

$ABS -- Takes a string-value, or numeric value converted to string, squeezes blanks and then strips leading minus (-), underscore (_), and plus (+) characters. The returned string is therefore devoid of leading "sign" characters. You can then convert them with $INT, $REAL, $PACK to numeric values (comparable numerically). Remember, string compares work through the length of the shorter string.

Thus: if 2 < 10 then ... is FALSE because only "2" and "1" are compared, and "2" is greater than "1" in the lexical character set. But if the left-operand is a numeric value, and the right operand is string, the the right operand is converted to the same numeric type as the left operand.

For example: if $pack(2) < $abs('-10') then ... is TRUE because $ABS returns the string "10", but it is the right operand being compared to a numeric operand on the left. So "10" is converted to packed, and 2 is numerically less than 10.

7.2.0c  (The $ACCESSTEST Function)

--------------------------------------------------
Function: $ACCESSTEST
Purpose: See if current user appears in access
            list in named EXTDEF record
--------------------------------------------------

The $ACCESSTEST function checks to see if the current user is represented in the named EXTDEF record in the ACCESS-ACCOUNTS list or in any sublists named in the record. If $UNIVID is set for the current account, then the ACCESS-UNIVIDS list is also examined.

The form of the function is:

The "extdef-record-key" is the key of the EXTDEF record you want SPIRES to scan.

The function returns $TRUE (1) if the account is represented in the record, and $FALSE (0) if it is not.

When you first request this, SPIRES looks at the latest copy of the record in the EXTDEF subfile. However, it keeps the result cached internally, so for subsequent lookups in the SPIRES session to the same EXTDEF record, it does not re-examine the actual record.

7.2.1  (The $AMATCH Function)

--------------------------------------------------
Function: $AMATCH
Purpose: Match a value against a variable array
            in manner similar to $MATCH
Related functions: $APMATCH, $MATCH, $ARMATCH
--------------------------------------------------

The $AMATCH function matches a string against members of a static or dynamic variable array, letting you determine whether or not the string is represented in the array. [See Chapter 4 for variables and arrays.]

The form of the function is:

Note that all four of these arguments are required. The input string represents the value to be matched against the members of the array. Any of the patterns allowed in the $MATCH function are available for use in the members of the array. The second argument represents the name of the array to be examined. (Since this argument represents an array name and is usually not a variable itself, it should probably not be preceded with a pound sign "#".)

The third and fourth arguments specify the range of subscripts or members of the array to be searched -- they allow the user to specify that only a portion of the array is to be examined. [The function will return a value of zero if the specified upper-bound (the fourth argument) is lower than the lower-bound.]

The function operates in a manner similar to $MATCH. If nothing matches, a value of zero is returned. If a match occurs, then the value returned is the value of the subscript of the first value that matched. Note that since arrays may begin with a zero subscript, one can't distinguish between a match of the zero-subscripted member and no match at all. [See 4.3 for a method of skipping the "zero" occurrence in setting up the array.]

For example, suppose an array called VALUE has been defined so that #VALUE::1 is "ALPHA", #VALUE::2 is "BET?", #VALUE::3 is "?AMM?":

    -> show eval $amatch("ALPHA",VALUE,1,3)
    1
    -> show eval $amatch("BETA",VALUE,1,3)
    2
    -> show eval $amatch("GAMMA",VALUE,1,3)
    3

7.2.2  (The $APMATCH Function)

--------------------------------------------------
Function: $APMATCH
Purpose: Match a value against a variable array
            in the manner of $PMATCH
Related functions: $AMATCH, $PMATCH, $ARMATCH
--------------------------------------------------

The $APMATCH function matches an input string against members of a static or dynamic variable array, allowing you to determine whether the string matches all or part of a value in the array, doing "stem-matching". [See chapter 4 for more on variables and arrays.]

The form of $APMATCH is as follows:

All four of these arguments are required. The "input-string" is the string to be matched. The "array-name" is the name of the array to be examined. (Note that this is the name of the variable array and is probably not a variable itself; thus it probably should not be preceded by a pound sign "#".) The third and fourth arguments specify the range of subscripts or members of the array to be searched -- they allow you to specify that only a portion of the array is to be examined.

As $PMATCH corresponds to $MATCH, so does $APMATCH correspond to $AMATCH. If the input string matches a member of the array through the length of the input string, then the value returned is the integer value of the subscript of the first member that matched. If no match is found, a value of zero ("0") is returned. [A value of zero is also returned if the upper-bound (the fourth argument) is lower than the lower-bound (the third argument).]

For example, suppose an array has been set in which #X::1 is "SEARCH", #X::2 is "HELP", and #X::3 is "DISPLAY":

    -> show eval $apmatch(DIS,X,1,3)
    3
    -> show eval $apmatch(DISPLAYED,X,1,3)
    0
    -> show eval $apmatch(dis,X,1,3)
    0

"DIS" matches (through its entire length) one of the array values, but "DISPLAYED" does not. Note also that capitalization is significant in pattern-matching.

A warning: since arrays may begin with a zero subscript, you can't distinguish between a match with the zero-subscripted member and no match at all. [See 4.3 for ways to skip this "zero" occurrence in setting up an array.]

7.2.2a  (The $ARMATCH Function)

--------------------------------------------------
Function: $ARMATCH
Purpose: Match a value against a variable array
            in manner similar to $AMATCH
Related functions: $APMATCH, $MATCH, $RMATCH
--------------------------------------------------

The $ARMATCH function matches a pattern against members of a static or dynamic variable array, letting you determine whether or not the array contains anything fitting the pattern. [See Chapter 4 for variables and arrays.]

The form of the function is:

Note that all four of these arguments are required. The input string represents the value to be matched; any of the patterns allowed in the $MATCH function are available. The second argument represents the name of the array to be examined. (Since this argument represents an array name and is usually not a variable itself, it should probably not be preceded with a pound sign "#".)

The third and fourth arguments specify the range of subscripts or members of the array to be searched -- they allow the user to specify that only a portion of the array is to be examined. [The function will return a value of zero if the specified upper-bound (the fourth argument) is lower than the lower-bound.]

The $ARMATCH function works better than $AMATCH in situations where the array is loaded with values that might contain multiple words (say, a list of EXPLAIN terms); the user is supplying the term he or she wants explained; and you'd like to allow the user to specify the first few letters of each word in the term, if desired, as in EXPLAIN SYS VAR for "SYStem VARiables". You could change the blanks within the value to a "? " (question mark, followed by a blank) and end the value with a "?" so that the pattern to match in this example would be "SYS? VAR?", which would indeed match to the array member "SYSTEM VARIABLES" if it existed.

The function operates in a manner similar to $MATCH. If nothing matches, a value of zero is returned. If a match occurs, then the value returned is the value of the subscript of the first value that matched. Note that since arrays may begin with a zero subscript, one can't distinguish between a match of the zero-subscripted member and no match at all. [See 4.3 for a method of skipping the "zero" occurrence in setting up the array.]

For example, suppose an array called VALUE has been defined so that #VALUE::1 is "ALPHA BITS", #VALUE::2 is "BETA TEST", #VALUE::3 is "GAMMA RAY":

    -> show eval $amatch("ALPHA?",VALUE,1,3)
    1
    -> show eval $amatch("B? T?",VALUE,1,3)
    2

7.2.2b  (The $ASCII Function)

--------------------------------------------------
Function: $ASCII
Purpose: Convert EBCDIC to ASCII or visa-versa.
Related procs: $ASCII.IN, $ASCII.OUT
--------------------------------------------------

This function has two string parameters and creates a string result. The first parameter is a conversion code, of which only the first character is examined. If the first parm is null, "I" is assumed. The second parameter is a string to be converted either from ASCII to EBCDIC, or EBCDIC to ASCII. If the second parm is null, the result is null regardless of the conversion requested by the conversion code.

The conversions codes of "I" or "A" convert to ASCII.

The conversions codes of "O" or "E" convert to EBCDIC.

The "I" and "O" codes correspond to the $ASCII.IN and $ASCII.OUT processing rules. Thus, $ASCII(In,"input string") converts the "input string" from EBCDIC to ASCII. The "A" and "E" codes can be used to indicate the result of the conversion. For example, $ASCII(A,"input string") also converts from EBCDIC to ASCII.

Other codes may be introduced in the future, for other reasons, so please just use "A" or "I" to convert from EBCDIC to ASCII, and "E" or "O" to convert from ASCII to EBCDIC.

7.2.3  (The $ASET Function)

---------------------------------------------
Function: $ASET
Purpose: Set values in a static array
Related functions: $AMATCH, $APMATCH,
   $DYNASET
---------------------------------------------

The $ASET function lets you set the value of occurrences in a static array. [See Chapter 4 for variables and arrays.]

The function has three forms:

"Array-name" is the name of the array (and should generally not be preceded by a pound sign "#"). The "value" is the value to be stored, and "low" and "high" represent the beginning and ending occurrences of the array to be used for storage. (I.e., the value will be stored into each entry of the named array from the "low" occurrence to the "high" occurrence.)

If "high" is not given, the highest occurrence of the array is assumed. If "low" is not given, as in the last form, occurrence "0" is assumed; therefore, all occurrences in the array would be set to "value". [If the given value for "high" is lower than that for "low", the function returns a value of zero ("0").]

The function is often used with the EVAL command. [See 5.16.] The value returned by the function is an integer count of the number of array entries that were "set" by the $ASET function.

These same capabilities are also available for dynamic arrays through the $DYNASET function.

7.2.3.B  (The $BLDLCTR and $UBLDLCTR functions)

The $BLDLCTR function enables you to generate a SPIRES file residual locator from its components described below. $BLDLCTR returns a four byte hex value if the arguments are legitimate, otherwise a null value is returned.

$BLDLCTR -- This is a three argument function of the form.

where:

[EXPLAIN RES-LARGE STATEMENT.]

The $UBLDLCTR function enables you to "unpack" a residual locator to view its components in a more understandable way.

$UBLDLCTR -- is a two argument function which returns a value of type string.

where:

Consider the following examples:

For additional information about loactors:

[EXPLAIN TYPE=LCTR STATEMENT RECORD-TYPE.]

7.2.3.D  (The $BNF Semantics)

Documentation about $BNF semantics. This is for BNF writers.

There are 32 slots defined that hold code/values Parsed by $BNF. These slots are numbered from 0 thru 31. Slot 0 is reserved for error returns, so only slots 1 thru 31 should be used by the BNF.

Include the following lines at the start of the BNF source:

$SEM 00 GEN
$SEM 32 FLG
<n+FLG> where n ranges from 1 thru 31 is a set of semantic calls
      that do the following:
  1.  Check to see if the "code" for this slot is non-zero.
      If so, this semantic call fails.
  2.  If the "code" is zero, then this slot is set to one (1),
      and is remembered as the "last referenced" slot.  Other
      semantic processes may store into this slot.
<1+GEN>  This semantic checks that all input data has been used
      and, if so, it returns the numeric "answer" of $BNF(Parse..).
      This number is determined by the number of the right-part
      that successfully parsed.  For example, if (PERFORM) is
      successful in parsing on its first right-part, then number
      returned will be +1.  If there is "extraneous" data, then
      This semantic stored that data in slot-0, and returns a
      negative number, such as -1.  If the input didn't parse,
      then 0 is returned by $BNF(Parse,cmd,n).
<2+GEN>  Save all the input parsed by this right-part for the
      "last referenced" slot.  This defines the slot's "value".
      The corresponding "code" is set to 2.
<3+GEN>  This semantic sets a new value length to zero.  <4+GEN>
      and <5+GEN> accumulate value.  <6+GEN> stores the value.
<4+GEN>  Concatenate the string just parsed to the new value.
<5+GEN>  Concatenate the last character parsed to the new value.
<6+GEN>  Store the new value as the "value" of the "last referenced"
      slot.  The corresponding "code" is set to 2.
<7+GEN>  Checks that at most one of slots 7, 8 and 9 have a
      non-zero "code".  This semantic fails if more than one
      of those slots is non-zero.
<8+GEN>  Checks that either all input is used up, or the next
      character in the input of not alphanumeric.  This guarantees
      that a word ends without having additional letters/digits.
      For example, PIN <8+GEN> fails if the input is PINS.
<9+GEN>  Check a counter for zero.  If zero, <9+GEN> fails,
      otherwise the counter is reduced by one.  See <10+GEN>
<10+GEN> Increment a counter by one.  This counter is initialized
      to zero when $BNF(Parse...) is entered.  Typically it is
      used to keep track of nested parenthesis depth.  See <9+GEN>

[See 7.2.3c.]

7.2.3a  (The $ASORT and $ASORTX Functions)

---------------------------------------------------
Function: $ASORT, $ASORTX
Purpose: Sort a variable array ($ASORT) and
         assign sort order to index array ($ASORTX)
---------------------------------------------------

These two functions provide simple yet powerful ways to sort SPIRES variables. They will be particularly useful in formats, where they can be used in place of the less versatile sort facility there. See "SPIRES Formats", section B.10.8, for more information; online, [EXPLAIN SORTING VALUES IN FORMATS.]

Note that the last three parameters are optional.

The "array-name" is the name of the variable array whose values you want to sort. The array must be defined as part of a static vgroup that is currently allocated; however, it may be defined as dynamic. Do not precede "array-name" with a "#" (unless that variable itself contains the name of the array you want to sort).

The "index-array-name" in the $ASORTX function is the name of a companion array that will hold the occurrence values for the original array after the sort is complete. It should be an integer array that has at least as many occurrences as the number of values that will remain The original array is not changed. Hence, the new sort order of the original array is known indirectly by the values in the index array. See below for more information.

The "sort-code" is one of the following:

The "low" and "high" values are integers representing occurrence numbers within the original array to specify the range of occurrences to be sorted. If omitted, the entire array is sorted. If "low" is not specified, then occurrence "0" is assumed as the low end of the range. If "high" is not specified, then the highest occurrence number defined for the array is assumed as the high end of the range.

The function returns an integer value representing the number of entries that were sorted and that remain. Normally that value is equal to "high-low+1", but if you request elimination of duplicates from the sort, the value is reduced by the number of duplicates that were eliminated. If the value returned is "0", then an error occurred, e.g., the array name was invalid, or the "low" value was greater than "high". An array will always have at least one value to sort, even if that value is a null.

There are some important aspects of these functions to keep in mind: First, when sorting in ascending order, nulls sort first. If your array has 10 occurrences but you have populated the array with only five values, then the sorting will cause the first five occurrences to be null.

Second, when you choose an "eliminate duplicates" option, SPIRES will zap the values in the array (rather, to be precise, those that are in the "low-high" range) before repopulating the array with the sorted values. Note then that this may create "duplicate nulls" at the end of the array. This is presumably what you would expect; what makes it noteworthy, is that if the pre-sorted array had any nulls in it, those nulls would have been sorted to one null (i.e., eliminating the duplicate nulls), and that one null is part of the sorted data. If, for instance, the array had 10 occurrences but only five had non-null values, then an "ascending-delete-duplicates" sort would:

So the array would then have a null value for the first occurrence, then the four non-null values, and then null values in the remainder. Thus, SPIRES has created duplicate nulls.

But using the returned value helps identify the true remaining values in the array. The returned value from the function in our example was "5", meaning that the first five values in the array contain the unique, sorted values of the original array. Hence, only the first five occurrences of the array should be used when processing the array.

The $ASORTX function is useful when you don't want to change the order of the values in the original array. You define a companion array of type integer, having the same number of occurrences as the sorted array would have. For example, the ITEM array has a companion SORTNUM array, and they start with these values:

Because the value returned from the function is "4", the first four occurrences of the SORTNUM array now represent the sorted order of the non-duplicate occurrences in the ITEM array. (That is, a null value for the first occurrence, and the three unique non-null values.)

7.2.3b  (The $AUTHPARMS Function)

-------------------------------------------------
Function: $AUTHPARMS
Purpose: Passes to Routing parms needed for
         SUFSIGN or LOCAL signature validation
-------------------------------------------------

The $AUTHPARMS function is used in Prism routing applications, in order to pass to the Routing module parms needed for SUFSIGN or LOCAL authority checking (or "signature validation"). [EXPLAIN SUFSIGN for details about both SUFSIGN and LOCAL validation, including use of the $AUTHPARMS function]

7.2.3c  (The $BNF Function)

----------------------------------------------
Function: $BNF
Purpose: Parse input string into tokens
----------------------------------------------

The $BNF three-argument function provides several capabilities in terms of parsing input strings by BNF. To activate a particular BNF, it first must be "loaded" by either:

where "name" is a 1-to-8 character name to be assigned to the BNF, and "object" is the name of a card-image data set (under ORVYL) that is the $DECK output from the BNF ANALYZER. The "Load" option makes the loaded BNF the "primary" BNF. The "Tempload" option will also load a BNF and keep it in core, but it is a "one-shot" BNF. $BNF(Parse,cmd,num) checks for the Tempload BNF first, and if it is defined, uses Tempload to Parse. If Tempload is not defined, then the last Loaded BNF is used. When Parse is finished, Tempload is cancelled (no longer defined). Another $BNF(Tempload..) would have to be issued to redefine Tempload. If $BNF(Load..) or $BNF(Tempload..) fail, or $BNF(Parse..) can't find either Tempload or Load defined, then $BNF returns -999.

There are special "name" values that are checked for linked-in BNF modules. These names are of the form: "PRELOADn" where n varies from 1 thru 9. For example, $BNF(Load,PRELOAD1,object) will check if PRELOAD1 is a pre-linked entry point. If it is, the object deck is ignored and the pre-linked BNF is used.

Both Loaded and Temploaded BNFs remain in core even though they may not be actively defined as the "primary" or "one-shot" BNF. $BNF(Unload,name,) can be used to "unload" a named BNF from core. This action cancels the "primary" or "one-shot" that matches the BNF to be unloaded, and the core copy of the BNF is eliminated.

$BNF(Parse,cmd,num) uses the "num" (positiive integer) to choose a particular starting point in the Loaded or Temploaded BNF. The "cmd" string is Parsed by that particular BNF production. When the command string is not recognized, $BNF(Parse..) returns zero. If the command is recognized and succeeds in parsing, a positive integer is returned. If the command is recognized, but fails to parse completely, the negative equivalent of the integer returns. Note: -999 indicates that $BNF(Parse,cmd,num) failed for reasons other than command recognition, such as no active Load or Tempload, or "num" outside the range of 1 through 9.

If $BNF(Parse,cmd,num) succeeds in parsing (positive return), then a table of parameters is also established. These parameters and their values can be retrieved by:

$BNF(Get,Code,n) will return integers 0, 1, or 2 for the n-th parm. 0 means the parm was not given; 1 means it was given without a value; 2 means it was given with a value, which might be null.

$BNF(Get,Value,n) returns the string value of the n-th parm. If $BNF(Parse..) returns a negative number indicating a cmd was recognized, but that cmd had an error in parsing, then $BNF(Get,Value,0) will return the unparsed portion of the cmd. [See 7.2.3d.]

7.2.4  (The $BREAK Function)

-------------------------------------------------
Function: $BREAK
Purpose: Break input value at a point to the left
            of a character in a second string
Related functions: $PARSE, $SPAN, $STRIP
-------------------------------------------------

The $BREAK function can be used to "break" or truncate its input string at a specified point within the string. The form of the function is as follows:

$BREAK searches the input string until it finds one of the characters in string2. When it finds such a character it stops scanning the input string any further, and returns the characters of the input string up to but not including the non-matching character in string2. (String2 is thus a type of 'exclusion list', since the incidence of any character in this list stops the function from scanning any further.)

If no characters of the exclusion list occur in the input string, $BREAK returns the the entire input string.

For example, the following command stores the "prefix" to a value within a user-defined variable:

Or suppose $ASK contains the value FIND AUTHOR CAPOTE and the statement below is executed:

CMD will now contain FIND (the value of $ASK up to the first blank).

If you want the break characters in the value to be ignored when they are within "containing groups" (say, for example, you do not want to break the value at a comma if the comma is within a parenthetical expression), you might want to use the $PARSE function instead.

7.2.4a  (The $CAPITALIZE Function)

------------------------------------------
Function: $CAPITALIZE  ($CAP)
Purpose: Return an input value converted
   to upper case
Related function: $CASE
------------------------------------------

$CAPITALIZE (or $CAP for short) is a single-argument function that converts the referenced string to uppercase. For example:

It can be used, e.g., to ensure that values match when one of the values to be matched is in a different case from the other. For a function that offers more customized handling of upper and lower case, see $CASE.

7.2.5  (The $CASE Function)

------------------------------------------------
Function: $CASE
Purpose: Return input value converted to one of
   several upper and lower case formats
Related functions: $CAP, $CHANGE, $TRANSLATE
Related system procs: $CAP, $LOWER, $UPPER
------------------------------------------------

$CASE is a two-argument function that can be used to put a string value into one of several different forms of upper and lower case. It has the following syntax:

If no "option-number" is included, $CASE acts like $CAPITALIZE above. The possible option numbers match the values of the P1 parameter of the $CASE system proc (or A30 processing rule) which have the same purpose; the options and their numbers are:

For example, if #X has the value 'VIRGINIA is for LOVERS',

Note that a "word" here means any substring whose component parts are lexically greater than HEX 7F and which is delimited by characters that are lexically less than HEX 80. Thus alphabetic or numeric words separated by blanks, commas, or most other standard punctuation would be considered words as defined above.

There is one exception to the rule cited above, regarding apostrophes (hex 7D) when they appear within words. When surrounded by characters lexically greater than 7F, they are considered as part of the word rather than as a word separator. Hence, $CASE("peter's",10) returns "Peter's". Note too that though this is the most popular way for apostrophes to be treated, there are some situations where it is not so beneficial: $CASE("o'toole's",10) returns "O'toole's".

7.2.6  (The $CHANGE Function)

-----------------------------------------------
Function: $CHANGE
Purpose: Change occurrences of one specified
   string in input-value to a different string
Related functions: $CASE, $CHANGELIST, $DOUBLE,
   $TRANSLATE, $UNEDIT
-----------------------------------------------

$CHANGE is a three-argument function that examines the input string for any occurrences of string2, which are then changed to string3.

For example,

If string2 is null (or if string2 is longer than string1), then the input string remains unchanged.

7.2.6a  (The $CHANGELIST Function)

-----------------------------------------------
Function: $CHANGELIST
Purpose: Change occurrences of specified
   strings in input-value to different strings
Related functions: $CASE, $CHANGE, $DOUBLE,
   $TRANSLATE, $UNEDIT
-----------------------------------------------

$CHANGELIST is a three-or-more-argument function that examines the input string for any occurrences of string2, which are then changed to string3; that changed string is then examined for occurrences of string4, which are changed to string5, and so on.

For example:

The third example shows that a string can be changed to null (the dollar sign is changed to null; hence the "odd-numbered" parameters can be null. The last example shows what happens when you have an even number of parameters -- the error occurs because the parameter string must consist of the input string plus pairs of strings, which means an odd number of parameters.

As with $CHANGE, if any of the "even" strings (those being looked for) are longer than the input string, then no change will occur for that pair of strings; the same is true if an even string is null.

7.2.7  (The $CHARACTER Function)

------------------------------------------------
Function: $CHARACTER  ($CHAR, $TRIM)
Purpose: Return value converted to type CHAR
   (fixed-length string)
Related function: $STRING, $SQU
------------------------------------------------

The $CHARACTER (alias $CHAR and $TRIM) is a one-argument function that returns its argument converted to type CHAR (fixed-length string). It works similarly to the $STRING function except that when a character value is converted to a string (for instance by being prefixed by a slash "/"), any trailing blanks in the value are stripped away, as the example below illustrates:

In the example, the string represented by #x is actually 18 characters long, as the $SIZE function shows. However, converting the value of #x to a character-type variable #y before reconverting it to string effectively trims the blank characters. [See 6.2 for more information on CHAR versus STRING.]

The function $SQU also trims a string but trims leading and multiple blanks as well, and does not convert the value's type to CHAR.

7.2.7a  (The $CHECK Function)

----------------------------------------
Function: $CHECK
Purpose: Returns value of check digit
 being applied to the first input value
Related System Procs: $CHECK
----------------------------------------

The $CHECK function can be used in connection with check-digit processing to determine what check digit has been or will be applied to a given integer value, according to the chosen method of digit checking. [Note: Check digits are used as part of data input in certain files, as a way of guaranteeing that an integer value has been correctly input. For more information, EXPLAIN $CHECK PROC online, or see the manual "SPIRES System Procs".]

The form of the function is as follows:

    $CHECK(integer,code)

"Integer" represents the integer value whose check digit you wish to determine.

"Code" represents the method of check digit conversion that is being used. Values for "code" are as follows:

  Code    Method
  -----   -----------------------------
  M       Mod-11 rule
  S       Student Service (SS) rule
  E       Extended Student Service rule
  L       Luhn rule
  A       ABA rule

If you do not specify a correct code, the Mod-11 rule will be assumed.

The function returns the integer value of the check digit that applies to the integer input value, according to the check digit conversion method specified by "code". For instance:

7.2.7b  (The $CLRSUBF Function)

----------------------------------------
Function: $CLRSUBF
Purpose: Clear the named subfile as a
 a subgoal, and detach its file
----------------------------------------

The $CLRSUBF function can be used in situations where you want to free up some internal memory space used by subfile subgoals that will no longer be needed in the current situation. The subfile named in the function will be cleared from internal memory tables. In addition, the file it belongs to will be detached, as long as there are no other subfiles or subgoals active for that file.

Its syntax is:

where "subfile-name" is the name of the subfile. You may optionally specify the "filename" prefix if desired.

You may use this only to eliminate subfiles used in subgoals; it cannot be used to clear either the primary subfile or any subfiles selected through paths.

The function returns a "1" ($TRUE) if the function succeeds in clearing the named subfile; if the subfile is not cleared, the function returns a "0" ($FALSE).

7.2.7c  (The $COLUMNTEST Function)

----------------------------------------
Function: $COLUMNTEST
Purpose: Retrieve the element name with
 a matching Rdbms column name.
Related Functions: $ELEMINFO, $ELIDINFO
----------------------------------------

This single parameter function may be used to find a SPIRES data element name that corresponds to a given RDBMS column name. $COLUMNTEST examines the ELEMINFO packets for the currently Selected subfile for a column name. When the function locates a packet which has a matching value of the RDBMS_COLUMN field, then the corresponding data element name is returned.

The form of the function is as follows:

    $COLUMNTEST(string)

where "string" is a character string holding the Rdbms column name. The value returned is the name of the data element whose ELEMINFO packet specifies this column name. $ELEMID is set to the data element ID. If no match is found then a null value is returned.

7.2.8  (The $COMPARE Function)

-------------------------------------------
Function: $COMPARE
Purpose: Compare string value with second
   string, returning the part that matches
Related functions: $LEFTSUB, $RIGHTSUB
-------------------------------------------

The $COMPARE function has the following syntax:

where "string1" and "string2" are character strings and "character" is a single character. "String2" or "character" may be null, which may be indicated by omitting them, if desired.

If "string1" and "string2" do not have the same length, then the character is added to the end of the shorter value as many times as is necessary to make the two strings the same length. The two strings are then compared character by character until a mismatch is found. The value returned is the substring in common up to the point of the mismatch.

For example,

7.2.9  (The $DATEIN Function)

---------------------------------------------
Function: $DATEIN
Purpose: Process date value
Related functions: $DATEOUT, $DAYS, $XDATE
---------------------------------------------

$DATEIN takes a date that is in character string form (for instance, "January 5, 1986" or "2/6/85") and converts it into a 4-byte hexadecimal string of the form "CCYYMMDD" (such as "19860105" or "19850206"). The value created by $DATEIN from a date string is therefore (like the result of the system proc $DATE) automatically in the proper form for internal storage as a date.

The syntax is as follows:

Here "date-string" can be any valid input string for the $DATE proc, including the system variable $DATE or even output from the $DATEOUT function, described elsewhere. If the date-string should fail to convert properly, the function will return the four-byte hex representation '00000000'. As with any string, putting it in apostrophes is recommended; SPIRES will try to convert a value like 01/01/01 into arithmetic (treating the "/" as a division operator) without them.

One or more options as shown below may be added following the "date-string"; options should be separated by commas.

The following options control how SPIRES handles an input value that has a two-digit year value instead of a four-digit one ("95" instead of "1995", for example). By default, SPIRES will supply the century digits of the current date for the missing ones. Other options, of which you should specify only one, include:

Examples:

The input date may be a specific date (like July 4, 1776) or a general date whose specific value is relative to the current date, such as "today" or "Friday of next week". See the SPIRES manual "Searching and Updating" for a complete list of allowed forms; online, EXPLAIN DATES, FOR INPUT.

7.2.9.1  (The $DATEOUT Function)

---------------------------------------------
Function: $DATEOUT
Purpose: Convert a date value to one of
   several string formats
Related functions: $DATEIN, $DAYS, $XDATE
---------------------------------------------

$DATEOUT accomplishes more or less the opposite of the $DATEIN function, taking a date in hex form and generating from it a date in the more familiar character-string form.

$DATEOUT has the following form:

The "hex-date" can be any valid 4-byte hex-form of a date, such as $UDATE or the output from either the $XDATE or $DATEIN functions, or a value that has been processed by the $DATE system proc.

If there are no other arguments, SPIRES displays the date in the standard output form "MM/DD/CCYY" where "CC" is suppressed if it matches the current century. For example, if it is 1995:

The other arguments, if any, control the form of the output string, producing other date formats described below. In the older form of the function (shown second above), you specify the output form as a single integer, using the chart at the end of this description to determine which integer defines the form you want.

The newer form of the function, shown third above, lets you provide a list of descriptive keywords as options to specify the form of the date you want. These symbols match the symbols available on the $DATE.OUT system proc. In the $DATEOUT function, however, they may be specified in any order.

Keyword Options

Below are the keyword options for $DATEOUT's second and subsequent parameters. Note that they can be specified in any order, but must be separated from each other by commas. Some options are mutually exclusive, though no errors will result if a combination is self-contradictory -- SPIRES will simply choose one option over the other.

The table of examples that follows these descriptions may be the easiest way to determine which keyword combination you want.

The next two options, which change the order of the parts, are mutually exclusive; at most only one should be coded:

The next three options are mutually exclusive; no more than one should be coded:

The "century" options below are useful unless either MONTH or METRIC has been specified too, in which case these options have no effect whatsoever. The century options control whether SPIRES will display the first two digits (CC) of the year portion of the output value. By default, unless MONTH or METRIC is specified, SPIRES will suppress the first two digits of the year when they match the first two digits of the current year. That is, if the output value is "1902" and it is currently 1995, the year will be displayed as "02". These options are mutually exclusive; only one at most should be coded.

Other options:

Here are some examples of output from a $DATEOUT function, using two different output dates: February 1, 1992 and December 2001. All examples were created prior to 2000, when CC=19 was true. The keyword options that produce the output appear in the left column:

Keywords                  Feb 1, 1992           December 2001
-----------------------   --------------------  -----------------
(none)                    02/01/92              12/--/2001
DAY                       SAT.  02/01/92        ----- 12/--/2001
DAY,SQU,FULL,NODASH       SATURDAY, 02/01/92    12/2001
MONTH                     FEB.   1, 1992        DEC.  2001
CANADIAN                  01-02-92              12-2001
CANADIAN,MONTH,UPLOW       1 Feb. 1992          Dec.  2001
METRIC                    1992.02.01            2001.12
METRIC,MONTH,UPLOW        1992 Feb.  01         2001 Dec.
CCYY                      02/01/1992            12/--/2001
YY                        02/01/92              12/--/01
C90                       02/01/92              12/--/01
C95                       02/01/1992            12/--/01
CANADIAN,MONTH,SQU        1 Feb. 1992           Dec. 2001
DAY,MONTH,FIXED           SAT FEB  1, 1992      --- DEC 2001
DAY,MONTH,FULL,UPLOW      Saturday,             ----- December 2001
                           February 1, 1992

Integer Codes

To work with the older integer codes, or to convert from the old codes to the new ones, use the following chart, which shows the codes, a sample output string, and the keywords that can be specified to create the same value. Note that the integers match the integer values used by action A76, the Outproc rule, as its P1 parameter.

Integer Code   Sample Value           Keywords
 int= 0         02/01/92               (default)
 int= 1         FEB.   1, 1992         MONTH
 int= 2         SAT.  02/01/92         DAY
 int= 3         SAT.  FEB.   1, 1992   MONTH,DAY
 int= 4         01-02-92               CANADIAN
 int= 5          1 FEB.  1992          CANADIAN,MONTH
 int= 6         SAT.  01-02-92         CANADIAN,DAY
 int= 7         SAT.   1 FEB.  1992    CANADIAN,MONTH,DAY
 int= 8         1992.02.01             METRIC
 int= 9         1992 FEB.  01          METRIC,MONTH
 int=10         SAT.  1992.02.01       METRIC,DAY
 int=11         SAT.  1992 FEB.  01    METRIC,MONTH,DAY
 int=12         1992.02.01             (same as 8)
 int=13         1992 FEB.  01          (same as 9)
 int=14         SAT.  1992.02.01       (same as 10)
 int=15         SAT.  1992 FEB.  01    (same as 11)
 int=16         02/01/92              *FIXED
 int=17         FEB  1, 1992           FIXED,MONTH
 int=18         SAT 02/01/92           FIXED,DAY
 int=19         SAT FEB  1, 1992       FIXED,MONTH,DAY
 int=20         01-02-92              *FIXED,CANADIAN
 int=21          1 FEB 1992            FIXED,CANADIAN,MONTH
 int=22         SAT 01-02-92           FIXED,CANADIAN,DAY
 int=23         SAT  1 FEB 1992        FIXED,CANADIAN,MONTH,DAY
 int=24         1992.02.01            *FIXED,METRIC
 int=25         1992 FEB 01            FIXED,METRIC,MONTH
 int=26         SAT 1992.02.01         FIXED,METRIC,DAY
 int=27         SAT 1992 FEB 01        FIXED,METRIC,MONTH,DAY
 int=28         1992.02.01             (same as 24)
 int=29         1992 FEB 01            (same as 25)
 int=30         SAT 1992.02.01         (same as 26)
 int=31         SAT 1992 FEB 01        (same as 27)

                                      *=FIXED has no effect here;
                                        same as integer minus 16

If you add 32 to any of the integer codes from 1 to 11 above, the "month" and "day" words (such as JULY and THURSDAY) that appear will always be spelled out in full, excess blanks will be squeezed out (such as the blanks shown in the "int=3" example above), and a comma will be added after the "day" word. So, for instance, for "int=35" (3+32), the returned value might be "Thursday, July 1, 1954". (If you were using keywords, you would add the keywords FULL and UPLOW for the same effect.)

7.2.9.2  (The $DAYS Function)

---------------------------------------------
Function: $DAYS
Purpose: Return date value converted into an
   integer count of days
Related functions: $DATEIN, $DATEOUT, $XDATE
---------------------------------------------

$DAYS is a one-argument function that takes a date value (in the form CCYYMMDD, either as a 4-byte hex value or a literal character string) and returns an integer value representing the number of days since the arbitrary date Jan. 1, 0000. This value may not seem especially useful by itself, but it can be very useful in computations, for instance, if you have two date values in unconverted form, and need to know how many days have elapsed between them:

In the example below, the user-variable #DayNum will hold an integer value representing the number of days elapsed since January 1, 1901.

$DAYS can also be used to determine the day of the week of an input date string:

In the above example, if "x" is a date in character form, "y" is an integer from 0 to 6 representing the day of the week (0=Sunday through 6=Saturday). The $DATEOUT function might also be used to determine the same information.

See also the description of the $XDATE function, which converts dates in "day" form (e.g., date values processed by $DAYS) back into a standard date format.

A date value that doesn't contain a specific day of the month (such as "July 1991") will be treated as if it is the first day of that month. Similarly, a date value that is only a year (such as "1991") will be treated as if it is January 1st of that year.

7.2.9.3  (The $DATETIME Function)

---------------------------------------------
Function: $DATETIME
Purpose: Convert a datetime string to 8-byte
   hex format
Related functions: $DATEOUT
Related System Procs: $DATETIME.IN
---------------------------------------------

$DATETIME by default takes the value supplied in a standard form for a datetime (see below) and converts the value to an eight-byte hexadecimal string suitable for storage. A serious error is caused if the value is not in one of the standard datetime formats shown below, or if the value is not a valid datetime (e.g., "Feb 29, 1979" would cause an error; "Feb 29, 1980" would not). The function $DATEOUT can be used to reconvert the value for output. [See 7.2.9.4.]

Syntax and Parameters

$DATETIME converts a datetime_value into the eight-byte hexadecimal string of the form #CCYYMMDDTTTTTTTT# where CC is the century, YY is the year, MM the month, DD the day, and TTTTTTTT is the time as an integer. For example, "July 1, 1954 12:15:58" would be translated into the hex string #1954070102A1CC30#. Note that if the century is not supplied by the user, "20" (the current century) is assumed. The only exception is for metric dates such as "33.03.05 12:30". Over the next few years, as confusion over the meaning of 2-digit years grows, we recommend you use 4-digit years on input for clarity.

The input form of a datetime_value is a DATE followed by a separator followed by a TIME which must include a colon (:) character, or just a "date", NA, N/A, or NONE.

The "time" portion must be a "hh:mm:ss.fff" form, with "h:" as a minimum. Time consists of decimal digits, colon characters, and possible period character between seconds and fractions of seconds. Time range: "0:" through "23:59:59.999" which become 00000000 through 05265BFF as the time portion of the stored form (tttttttt). Values that exceed this amount cause the error flag to be set, and the value is replaced with the integer -1.

The input is scanned from right to left looking for the digits, colons, and periods. If colon is not found, the input form is assumed to consist of only a date. If a time is supplied, the separator between date and time may be any character other than digits, colons or periods. Blank is the preferred character, which is what will be output when the time portion of the stored form is not the integer -1 (indicating an unknown time).

The "date" portion may be any form of date allowed by $DATEIN with the NA option. That means dates without the century will default to the current century. You may also use "relative" dates, such as today, yesterday, tomorrow. N/A and NA and NONE are also accepted by this input process creating #99990909FFFFFFFF# as the 8-byte HEX form. If the "date" portion fails to convert properly, the resultant 8-byte datetime_value will be: #00000000FFFFFFFF#.

Data elements which hold DATETIME values probably should be defined with LENGTH = 8; The TYPE defaults to HEX if you use the inproc rule described below.

The default input form is: "mm/dd/ccyy hh:mm:ss", which is output by the $DATEOUT function or A76. [EXPLAIN A76 RULE FOR DATETIME.]

Specific date formats allowed include:

Most combinations of these work as well.

Relative date forms are also allowed, which express dates in terms of their relation to the current date. Here are some of the forms allowed:

Notice that the value always converts to a specific date, or a specific month and year, or a specific year. A value that would convert to a range of days within a month (such as "last week") will not be recognized, and will set the error flag.

A full description of allowed forms appears in the manual "SPIRES Searching and Updating" -- online, EXPLAIN DATES, FOR INPUT.

7.2.9.4  (The $DATEOUT Function for datetime)

---------------------------------------------
Function: $DATEOUT
Purpose: Convert a datetime value to one of
   several string formats
Related functions: $DATETIME
---------------------------------------------

$DATEOUT accomplishes more or less the opposite of the $DATETIME function, taking an 8-byte hex datetime value and converting it to a date in the more familiar character-string form, possibly followed by a blank and a time in hh:mm:ss[.fff] format.

$DATEOUT has the following form:

The "hex-datetime" can be any valid 8-byte hex-form of a datetime, such as the value created by the $DATETIME function, or a value that has been produced by the $DATETIME.IN system proc.

If there are no other arguments, SPIRES convertes the datetime value to standard output form "MM/DD/CCYY HH:MM:SS[.fff]" where [.fff] represents optional fractions of secords. The time-portion, and blank which preceeds it, will not be output if the datetime value has -1 as its integer time-portion. [See 7.2.9.3.]

The other arguments, if any, control the form of the date-portion of the output string, producing other date formats described below. In an alternate form of the function, you specify the output form as a single integer, using the chart at the end of this description to determine which integer defines the form you want.

The newer form of the function lets you provide a list of descriptive keywords as options to specify the form of the date you want. These symbols match the symbols available on the $DATETIME.OUT system proc. In the $DATEOUT function, however, they may be specified in any order.

Keyword Options

Below are the keyword options for $DATEOUT's second and subsequent parameters. Note that they can be specified in any order, but must be separated from each other by commas. Some options are mutually exclusive, though no errors will result if a combination is self-contradictory -- SPIRES will simply choose one option over the other.

The table of examples that follows these descriptions may be the easiest way to determine which keyword combination you want.

The next two options, which change the order of the parts, are mutually exclusive; at most only one should be coded:

The next three options are mutually exclusive; no more than one should be coded:

Other options:

SPIRES will convert the special date "09/09/9999" to N/A (for Not Applicable) on output. Any time-portion is suppressed when N/A is output. (Note: The output value "N/A" will always be uppercase, even if the UPLOW option is specified.)

Here are some examples of the date-portion of output from a $DATEOUT function. The keyword options that produce the output appear in the left column:

Keywords                  December 2001        December 5, 2001
-----------------------   -------------------  --------------------------
(none)                    12/--/2001           12/05/2001
DAY                       ----- 12/--/2001     WED.  12/05/2001
MONTH                     DEC.  2001           DEC.   5, 2001
CANADIAN                  12-2001              05-12-2001
CANADIAN,MONTH,UPLOW      Dec.  2001            5 Dec.  2001
METRIC                    2001.12              2001-12-05
METRIC,MONTH,UPLOW        2001 Dec.            2001 Dec.  05
CANADIAN,MONTH,SQU        DEC. 2001            5 DEC. 2001
DAY,MONTH,FIXED           --- DEC 2001         WED DEC  5, 2001
DAY,MONTH,FULL,UPLOW      ----- December 2001  Tuesday, December 5, 2001

Integer Codes

To work with the older integer codes, or to convert from the old codes to the new ones, use the following chart, which shows the codes, a sample output string, and the keywords that can be specified to create the same value. Note that the integers match the integer values used by action A76, the Outproc rule, as its P1 parameter.

Integer Code   Sample Value           Keywords
 int= 0         02/01/1992             (default)
 int= 1         FEB.   1, 1992         MONTH
 int= 2         SAT.  02/01/1992       DAY
 int= 3         SAT.  FEB.   1, 1992   MONTH,DAY
 int= 4         01-02-1992             CANADIAN
 int= 5          1 FEB.  1992          CANADIAN,MONTH
 int= 6         SAT.  01-02-1992       CANADIAN,DAY
 int= 7         SAT.   1 FEB.  1992    CANADIAN,MONTH,DAY
 int= 8         1992-02-01             METRIC
 int= 9         1992 FEB.  01          METRIC,MONTH
 int=10         SAT.  1992.02.01       METRIC,DAY
 int=11         SAT.  1992 FEB.  01    METRIC,MONTH,DAY
 int=12         1992.02.01             (same as 8)
 int=13         1992 FEB.  01          (same as 9)
 int=14         SAT.  1992.02.01       (same as 10)
 int=15         SAT.  1992 FEB.  01    (same as 11)
 int=16         02/01/1992            *FIXED
 int=17         FEB  1, 1992           FIXED,MONTH
 int=18         SAT 02/01/1992         FIXED,DAY
 int=19         SAT FEB  1, 1992       FIXED,MONTH,DAY
 int=20         01-02-1992            *FIXED,CANADIAN
 int=21          1 FEB 1992            FIXED,CANADIAN,MONTH
 int=22         SAT 01-02-1992         FIXED,CANADIAN,DAY
 int=23         SAT  1 FEB 1992        FIXED,CANADIAN,MONTH,DAY
 int=24         1992.02.01            *FIXED,METRIC
 int=25         1992 FEB 01            FIXED,METRIC,MONTH
 int=26         SAT 1992.02.01         FIXED,METRIC,DAY
 int=27         SAT 1992 FEB 01        FIXED,METRIC,MONTH,DAY
 int=28         1992.02.01             (same as 24)
 int=29         1992 FEB 01            (same as 25)
 int=30         SAT 1992.02.01         (same as 26)
 int=31         SAT 1992 FEB 01        (same as 27)

                                      *=FIXED has no effect here;
                                        same as integer minus 16

If you add 32 to any of the integer codes from 1 to 11 above, the "month" and "day" words (such as JULY and THURSDAY) that appear will always be spelled out in full, excess blanks will be squeezed out (such as the blanks shown in the "int=3" example above), and a comma will be added after the "day" word. So, for instance, for "int=35" (3+32), the returned value might be "Thursday, July 1, 1954". (If you were using keywords, you would add the keywords FULL and UPLOW for the same effect.)

7.3.1  (The $DECIMAL Function)

---------------------------------------------
Function: $DECIMAL
Purpose: Create packed decimal value
Related functions: $EDIT/$UNEDIT, $PACKED,
   $PRECISION, $REMAINDER, $WINDOW
---------------------------------------------

The $DECIMAL function is used to create a packed decimal value with a given number of decimal places. Its syntax is:

where "value" is a packed decimal value, or a value that can be converted to packed, and "decimal-places" is an integer from -155 up through 128, though it normally ranges from 0 to 30.

For example:

           value      decimal-places      result
         -------      --------------      ------
               3        2                   3.00
         476.032        2                 476.03
         476.038        2                 476.04

As you can see, $DECIMAL changes the precision of the value, adding precision in the first example, and subtracting it in the second and third. When precision is subtracted, rounding of the value may occur, as demonstrated in the third example.

Note that if the precision of the result is greater than 30, the result will be "infinity" (@@); on the other hand, if the precision of the result is 0 (zero), the result is 0.

If the input value is 0 or positive or negative infinity, it will not be changed by $DECIMAL.

7.3.2  (The $DEFQTEST Function)

------------------------------------------------
Function: $DEFQTEST
Purpose: Retrieve information about a subfile's
   deferred queue
Related function: $RECTEST
------------------------------------------------

The $DEFQTEST function lets you determine what type of data, if any, is in a file's deferred queue. (This data might consist of update transactions for goal records in each subfile defined by the file definition, log records for subfiles with command logging, batch requests, or even update transactions for other record-types in the file.) $DEFQTEST is valid only when the file in question is "attached" -- that is, either you have SELECTed one of its subfiles or used the ATTACH command to access record-types in the file.

The form of $DEFQTEST is

where "type" is one of the following:

For the types listed above (other available types are described below), $DEFQTEST returns the following integer values:

For example, suppose the file GA.JNK.RECORDINGS has two subfiles, RECORDS and SELECTIONS, and several records have been added to RECORDS; neither subfile has command logging; no batch requests have been submitted. Depending on which subfile you select, you will get the following values for $DEFQTEST:

Since $DEFQTEST(GOAL) looks in the deferred queue for goal records of the particular subfile selected, you are told that there is some update transaction in the RECORDS subfile and none in the SELECTIONS subfile.

Other types allowed are:

For these two types, if the integer 1 is returned, the flag is set. If the integer 0 is returned, the flag is not set.

The following two types allow you to determine the date and time that the deferred queue was last reinitialized (either through SPIBILD processing or through the ZAP DEFQ command):

All types return a -1 integer value if no subfile is currently attached, or if an error occurs during function processing.

Another type is:

If the returned value is 0, then the subfile has not been disabled. Other returned values and their meanings:

Note, however, that only users with Master access to the file will ever see the value "3" -- others will get an S555 error, with the file being detached, which is the purpose of the setting.

A related type is:

If the returned value is 0, then file updates are allowed (though subfile restrictions such as SSW 3 may be in effect that prevent the current user from updating the file). Other returned values and their meanings:

For any of the 4 non-zero responses, you can check $SNUM to get further information for the problem -- $SNUM holds the error code. Common reasons for a returned value of "4", for instance, are S495 (NOJOBGEN flag is set) or S560 (the deferred queue has been built with FASTBILD and needs to be either processed or "zapped").

Still other types:

If the returned value is 0, then the file can be processed in SPIBILD as normal; if it is 1, then the file owner has disabled file processing with the SET SUBFILE DISABLE FOR PROCESS command.

A file owner can choose to have a duplicate deferred queue created for a file, so that each transaction goes into the normal defq as well as a copy. See the SPIRES manual "File Definition" for details; online, [EXPLAIN DUPLICATE-DEFQ.]

If this feature is turned on and duplication is occurring, then the returned value from this function is 0. Other returned values:

     1 = duplication has been disabled by the file owner
          (using the SET SUBFILE DISABLE FOR DUPLICATE command)
     2 = duplication has been re-enabled by the file owner,
          but won't actually start again till the defq has been
          re-initialized (e.g., after SPIBILD processing)
    -1 = the file does not have defq-duplication

7.3.2a  (The $DENCODE Function)

--------------------------------------------------
Function: $DENCODE
Purpose: Encode or Decode values.
Related procs: $ENCRYPT
--------------------------------------------------

This function has two string parameters and creates a string result. The first parameter is a string you wish to encode or decode. If the first parm is null, the resulting string is null. The second parameter is a string that acts as an encryption key and is similar to the SET CRYPT value. If the second parm is null, a default encryption key is used. For a non-null key, only the first 8 characters are used.

$DENCODE is a bi-directional encode/decode process.

If you are trying to encode, the item should be a normal text string, but the result will be a gibberish string with unprintable characters. Therefore, it is recommended that you retype to a HEX-string.

The answer should be: 4C2BEA37EEB4011B6A152092540CAC27321A7A7678

If you are trying to decode, the item should be the encoded string, which means it must be the gibberish string returned by the encode. Therefore, if you retyped to a HEX-string, you will have to reconvert.

Of course the key for decoding must be the same as the encoding key.

$DENCODE acts like the $ENCRYPT(LEN) proc with SET CRYPT key.

7.3.3  (The $DOUBLE Function)

----------------------------------------------
Function: $DOUBLE
Purpose: Duplicate a string value
Related function: $CHANGE
----------------------------------------------

The $DOUBLE function has the following form:

The function causes the incidence of a substring (string2) of the input string to be duplicated wherever it occurs.

The $DOUBLE function can make it easier to add a SPIRES record in which quotation marks occur; in the example below, $DOUBLE would double the quotation mark every time one occurred in #X:

In a Uproc within a format or USERPROC, the punctuation would be slightly different:

7.3.4  (The $DYNASET Function)

---------------------------------------------
Function: $DYNASET
Purpose: Set values in a dynamic array
Related functions: $AMATCH, $APMATCH, $ASET
---------------------------------------------

The $DYNASET function is used to set values into a dynamic array. [See Chapter 4 for variables and arrays.]

Its syntax is:

"Array-name" is the name of the array (and should generally not be preceded by a pound sign "#"). The "value" is the value to be stored, and "low" and "high" represent the beginning and ending occurrences of the array to be used for storage. (I.e., the value will be stored into each entry of the named array from the "low" occurrence to the "high" occurrence.) All four arguments are required. [If the given value for "high" is lower than that for "low", the function returns a value of zero ("0").]

The function is often used with the EVAL command. [See 5.16.] The value returned by the function is an integer count of the number of array entries that were "set" by the function.

These same capabilities are also available for static arrays through the $ASET function, but $ASET does not require all four arguments.

7.3.4a  (The $DYNAZAP Function)

---------------------------------------------
Function: $DYNAZAP
Purpose: Destroy one or more members of a
          dynamic array
Related functions: $DYNZAP, $ZAP
---------------------------------------------

The $DYNAZAP function is used to eliminate multiple adjacent members of a dynamic variable array. [See Chapter 4 for variables and arrays.]

Its syntax is:

"Array-name" is the name of the array, without the pound sign "#". The "low" and "high" integers specify the beginning and ending occurrences of the array to be zapped. If you're not sure of the lowest and highest numbers of the array when you want to eliminate all members, use "0" for the "low" and any negative number (such as "-1") to represent the highest numbered occurrence. In other words, $DYNAZAP(array-name,0,-1) would zap the entire array, regardless of occurrence numbers.

The value returned by the function is an integer count of the number of array entries that were zapped by the function. If the function returns "-1", then either the "high" was greater than 32757, or the "low" was greater than the "high" (invalid unless "high" is negative, as described above), or the "array-name" was null or longer than 256 characters.

7.3.5  (The $DYNGET, $DYNPUT, and $DYNZAP Functions)

-----------------------------------------------
Functions: $DYNGET, $DYNPUT, $DYNZAP
Purpose: Control dynamic variables (e.g., from
          within a format or USERPROC)
Related functions: $DYNASET, $ZAP, $STATPUT,
   $STATGET, $VARPUT, $VARGET
-----------------------------------------------

The $DYNGET, $DYNPUT, and $DYNZAP functions help control dynamic variables from inside SPIRES formats, file definition USERPROCs, or protocols where LET statements would be inappropriate.

The forms of the functions are:

"Variable-name" is the name of the variable to be processed -- its form is analogous to that of the variable named in a LET statement. (Normally, the name is not preceded by a pound sign "#".)

"Subscript" is the subscript of the variable; a subscript of "0" is used if the variable is not in an array.

The third argument varies depending on the function.

The function $DYNGET allows a user to retrieve the value of a dynamic variable from inside a format. In a protocol, it allows a user to see if a variable with a particular name exists. If the named variable does not exist, then the function returns the default-value.

The function $DYNPUT allows a user to make a dynamic variable from inside a format. The third argument is the value to be assigned to the variable. The function returns the value of the third argument. For example:

will assign the value 5 to both #X and #Z.

The function $DYNZAP provides the ability to zap a dynamic variable from inside a SPIRES format. In protocol mode, the $ZAP function can be used. The third argument is null, i.e. ''.

7.3.6  (The $EDIT Function)

-----------------------------------------------
Function: $EDIT
Purpose: (Re)format value according to an
   edit mask
Related functions: $DECIMAL, $PACKED, $UNEDIT
Related system proc: $EDIT
-----------------------------------------------

The $EDIT function lets you "edit" a packed decimal field against an edit mask. Editing is most commonly performed on dollar-and-cent values to place the "$", "," and "CR" symbols in the appropriate places. The function works much like the $EDIT system proc. [For more information on edit masks, see the manual "SPIRES Technical Notes" or online EXPLAIN EDIT MASKS; for more information on the $EDIT proc, see the manual "SPIRES System Procs" or EXPLAIN $EDIT PROC.]

The function has the following form:

where

$EDIT returns a STRING value.

Some examples of the $EDIT function follow:

The length of the returned value is always the length of the edit mask. If the input value is too long for the edit mask, digits will be removed from the front of the value till it fits:

Make sure your edit mask is large enough for all possible values.

Note one difference between the $EDIT system proc and the $EDIT function: the input value for the function needs to be convertible to a packed value but its data type need not be packed or integer. (For instance, a string value of "12345" would be accepted.) The system proc, on the other hand, tends to be stricter about the data type of the input value.

Remember also, in general, that in situations where you could use either a function or a similar system proc, the proc is more efficient.

7.3.7  (The $ELEMINFO and $ELIDINFO Functions)

----------------------------------------------
Functions: $ELEMINFO, $ELIDINFO
Purpose: Access element information for the
   currently selected file
Related function: $INDEXINFO
----------------------------------------------

The $ELEMINFO and $ELIDINFO functions return a value from the occurrence of one of the info-elements in an element information packet. You may include either the element name or element-id in the function.

The syntax of $ELEMINFO is:

where "element-name" is the name or an alias of a real, virtual or phantom element defined for the selected subfile. It may be a fully qualified name (e.g. "str@str@elem").

The syntax of $ELIDINFO is:

where "element-ID" is the four-byte hex ID of the element. This ID can be found in the $ELEMID variable.

If the element named does not exist, the function returns a null value.

"Info-element" is the name or an alias of one of the info-elements. This parameter specifies what information you want to see from the element information packet. Possible values are:

Any value that contains a hyphen must be enclosed in apostrophes.

"Occurrence" is an optional parameter. It is a value, starting from 1, that says what occurrence of the specified information element is to be accesssed. (The only info-elements that are multiply-occurring are DESCRIPTION, COL-HEADING, INDEX, and USERINFO.) If you do not give a value, the function will retrieve the element's first occurrence.

The "default" parameter is also optional; it allows you to specify a value to be returned if the specified occurrence of the information element does not occur.

For example,

will retrieve the "width" info-element for the element named "age". If no value exists for width, the function will return the string "none".

The example below shows one way $ELIDINFO and the $ELEMID variable could be used for accessing an element by its element-id instead of its name:

   -> show eval $eleminfo(title,heading,1,none)
   Title of Book
   -> show eval $elidinfo($elemid,description,1,none)
   (Description info-element for TITLE element is displayed,
   if it exists)

Accessing an element by its element-id is more efficient than accessing it by name. [For more on the $ELEMID variable, see the manual "SPIRES Formats".] ;

7.3.8  (The $ELEMTEST, $ELNOTEST and $ELIDTEST Functions)

--------------------------------------------------
Functions: $ELEMTEST, $ELIDTEST, $ELNOTEST
Purpose: Retrieve information about a given
   element, such as its stored type or security
--------------------------------------------------

The three functions $ELEMTEST, $ELNOTEST, and $ELIDTEST, help obtain information describing a named element in the selected subfile's goal record or in an accessed subgoal record.

The syntax is as follows:

where "element-name" names an element in the selected subfile's goal or accessed subgoal. ("Element-number" refers to the element's numerical position within its record counting from zero, while "element-ID" refers to the element's four-byte hex elemid -- see the end of this section for more information on "element-number" and "element-ID".) If an element is not specified (i.e., if the first parameter is null for $ELEMTEST and $ELNOTEST; if the first parameter is $HEX(00000000) for $ELIDTEST), the first element in the file is assumed. The element named may be a dynamic element, with the following values for "code": TYPE, STRUCTURE, PART, and NAME.

Possible values for "code" are the same for all three functions, and are described in great detail below.

The function returns a null value if the element-name is not valid, if the code is illegal, or if no subfile is selected when the function is executed.

The "Code" Parameter of the $ELEMTEST Function

For $ELEMTEST (and $ELNOTEST and $ELIDTEST), the "code" parameter is what determines the information that the function supplies. Possible codes include the element's stored TYPE, its OCCURRENCE attribute in the file definition, its primary NAME and ALIASES, the SECURITY level set for it, and much more.

General note about $ELEMTEST: If an element is hidden, then this function fails, since the element does not appear as an element in the selected subfile.

The $ELNOTEST function

The $ELNOTEST function has the form:

where "element-number" is the number of the element within the record. For example, the first element of a goal record is element 0, the second is 1, and so forth. The first element within a structure is element 0. Elements within structures are identified by the element number of the structure, followed by an "@" sign, followed by the number of the element within the structure. Thus, the third element within the structure which itself is the second element in a record would be identified as "1@2". Note that because "@" is a special character, you must enclose the parameter value in quotation marks or apostrophes, e.g., $ELNOTEST('6@3',NAME).

The $ELIDTEST function

The $ELIDTEST function has the form:

where "element-ID" is a four-byte hex value identifying an element within a given record type. [Compare the $ELEMID variable, documented in the manual "SPIRES Formats".] Accessing an element by its element-ID is more efficient than using the element name, and therefore $ELIDTEST is used in general file formats such as $REPORT and $PROMPT, but elsewhere is used less commonly than $ELEMTEST or $ELNOTEST.


7.3.8a  (The $ENCIPHER Function)

-----------------------------------------
Function: $ENCIPHER
Purpose: Return a random (enciphered)
   hex value from the input string value
-----------------------------------------

The $ENCIPHER function takes an input string value and returns it as a coded (enciphered) hex value. Since there is no way currently known to decipher this hex value back into its original value, the function provides an effective method for enciphering data and hiding its original value.

The form of the function is as follows:

where "input-value" is the string value to be enciphered or hidden (for instance, a user's private ID). The function returns an 8-byte hex value (16 hex digits) that is a randomization of the value input. For a given input value, the function would always return the same enciphered value.

For example:

    -> show eval $encipher(123)
    4B7B2996CAE2E90F

Uses for $ENCIPHER

$ENCIPHER should be most useful for storing and validating values (such as personal ID numbers) that are too private to be seen in uncoded form. For instance, an application prompting users for personal ID would have to "encipher" the value before storing it or testing it, so that owners of the application would never see the real value. But the application could still verify the ID, for instance, by performing a lookup in a central ID file, where the ID values would be stored in enciphered form. As long as these enciphered values match, the original ID values also match.

For methods of encrypting data through a file definition, EXPLAIN SET CRYPT COMMAND and EXPLAIN $ENCRYPT PROC.

7.3.8b  (The $ENCODE and $DECODE Functions)

------------------------------------------------
Function: $ENCODE and $DECODE
Purpose: Encode/Decode strings
------------------------------------------------

The $ENCODE and $DECODE functions both $FUN records in RECDEF. $ENCODE takes a single string argument and returns an encoded result as a HEX-string. $DECODE takes the HEX-string and returns the original string that was encoded. For example:

What is interesting to note is that $ENCODE will return different answers for the same original string, but all of them will $DECODE back to the original string. Here's an example:

As you can see, the HEX-strings are different in these two examples, but the original string is "apple" in both. The $DENCODE function is used by both $ENCODE and $DECODE, making use of a changing key.

7.3.9  (The $EVALUATE Function)

-----------------------------------------
Function: $EVALUATE ($EVAL)
Purpose: Evaluate an expression
-----------------------------------------

The $EVALUATE function evaluates expressions, much like the slash prefix ('/'), and is most useful within FORMAT or USERPROC coding where the '/' is not available. [See 1.3, 5.17.]

$EVALUATE is a single argument function of the form:

The input string is assumed to be an expression consisting of variables, functions, operators, and other strings. $EVALUATE evaluates the expression, then returns this value converted to a string.

Some examples follow:

Note that in the second example, $EVALUATE first substitutes a value for the variable, then evaluates this value.

Keep in mind that $EVALUATE returns a string value:

The string "11" sorts before ("is less than") the string "2". You could fix this with the $INT function in this case:

[See 5.1.2.]

7.4.1  (The $EXP Function)

---------------------------------------------------------
Function: $EXP
Purpose: Raise a packed value to the specified power
Related functions: $LOG, $SQRT
---------------------------------------------------------

$EXP is a two-argument function of the form:

The first value is raised to the power specified by the "integer-value". For instance, $EXP(2,3) returns the value of 2 raised to the third power. The value returned by $EXP is of type PACKED.

Some examples of the $EXP function follow:

7.4.1b  (The $FACTORIAL Function)

------------------------------------------------
Function: $FACTORIAL
Purpose: Compute n!
------------------------------------------------

The $FACTORIAL function takes a single integer input, n, and computes n! creating a string answer that is acceptable as a packed value. n values larger than 27 show exponential notation. Negative input values unconditionally return 1. Example:

7.4.1c  (The $FACTORS Function)

------------------------------------------------
Function: $FACTORS
Purpose: Compute the prime factors of an integer
------------------------------------------------

The FACTORS function takes an integer argument, and the result of the function is a string of the argument's prime factors. If there are two or more such factors, they are separated by comma-blank. An individual factor may occur multiple times, represented by showing the factor, an apostrophe, and an integer number of times. For example, 8 has 2 as a prime factor three times, thus 2'3 represents the factor. Examples:

The FACTORS function uses primes up to 3571 to determine the factors of the input value. This allows for extremely large integers to be factored, and primes up to 12,666,481 can be determined. Values larger than that can also be processed if they have more than one prime factor.

7.4.2  (The $FLAG and $NOT Functions)

------------------------------------------------
Function: $FLAG, $NOT
Purpose: Convert value to type FLAG
------------------------------------------------

The $FLAG function has the following form:

Executing the function converts the argument to type flag, which has values of $TRUE and $FALSE (1 or 0).

Arguments of type STRING, CHAR and HEX convert to $FALSE if they are of null length. (Otherwise they convert to $TRUE.)

Arguments of type INTEGER, LINE, REAL and PACKED convert to $FALSE if they are 0. (Otherwise they convert to $TRUE.)

The $NOT function has the following form:

Like the $FLAG function, $NOT converts the argument to type flag, but returns the opposite logical value compared to $FLAG. Thus, if $FLAG(argument) is $TRUE, $NOT(argument) is $FALSE, and vice versa.

$NOT can be used as a replacement for '~' in IF, WHILE, or UNTIL statements.

7.4.2a  (The $FRAMETEST Function)

-------------------------------------------------
Function: $FRAMETEST
Purpose: Retrieve information about frames in
   the currently set format
-------------------------------------------------

The $FRAMETEST function is useful for determining the usage, direction, type, and dimensions of a frame within a currently set SPIRES format.

The function's syntax is as follows:

"Frame-name" is the name of a frame of the currently set format. It may also be a negative integer indicating the positive ordinal into the frame table. Thus, -1 means the 1st frame, -2 the 2nd, etc.

"Format-type" is FILE (to test the frame of a file-specific format), GLOBAL (to test the frame of a global format). If you leave this option blank, FILE format is assumed.

"Code" is one of the the following set of values:

  USAGE     - to return the USAGE value of the frame
  DIRECTION - to return the direction of the frame
  TYPE      - to return frame type
  ROWS      - to return number of rows for the frame (from FRAME-DIM)
  COLS      - to return number of columns for the frame (from FRAME-DIM)
  NAME      - to return the frame's name (useful with negative integers)

If no FRAME-DIM statement is coded for a frame, the function returns a value of -1 for the ROWS and COLS codes. Note that the $FRAMETEST function cannot currently be used within a format. Also note that TYPE always returns a non-null answer unless there is no frame associated with the given frame-name or ordinal position.

7.4.2b  (The $GETELOG Function)

-----------------------------------------
Function: $GETELOG
Purpose: fetch a piece of data from the
   error log
-----------------------------------------

$GETELOG is a two-argument function that retrieves data stored in the error log when it has been established and initialized by a SET ELOG command. [See 8.4.3.]

The function's form is as follows:

where

Only the first four of these items are available for every row of the error log; the others are optional, and will return a null value if they do not exist for the requested row.

So, for example,

The last SHOW EVAL command above shows what happens when you request a line from the log beyond its boundary; at the moment, the log was only 4 lines long, so a null value was returned.

If you specify "row-number" as a negative integer, the function will help you locate rows containing the code-types you are looking for. Specifically, the function will convert the integer to a positive number (in other words, the absolute value of the negative integer) and begin looking in that row for the requested code. If it does not find an occurrence of the requested code, it will look in the next row of the error log, and so forth. When it does find an occurrence, the function returns the row number in which it appeared, not the value it found. To see the value it found, replace the negative row-number in the function with the returned value.

For example, continuing the session above:

In the example above, we start looking for message lines containing $UCODE. Starting with row 1 of the log (as indicated by "-1" in the function), we are told there's an occurrence in row 1. After we retrieve that occurrence ("Invalid date"), we tell SPIRES to start in row 2 (indicated by "-2") and look for the next one, which we are told is in row 3, and so on.

7.4.2c  (The $GETPARMS Function)

-----------------------------------------
Function: $GETPARMS
Purpose: assigns stored values from the
   $SETPARMS function to named variables
Related functions: $SETPARMS, $REF
-----------------------------------------

$GETPARMS is a multi-argument function that retrieves values saved internally by the $SETPARMS function. It is generally used to pass parameters between a program and subroutines.

The function's form is as follows:

where

7.4.3  (The $GETxVAL Functions: $GETUVAL, $GETCVAL, $GETIVAL, $GETXVAL)

---------------------------------------------
Functions: $GETUVAL, $GETCVAL, $GETIVAL,
            $GETXVAL
Purpose: Retrieve element occurrences from a
   referenced record
---------------------------------------------

The $GETxVAL functions provide a way to access elements directly within records. The $GETUVAL and $GETIVAL functions retrieve unconverted, internal values, while the $GETCVAL and $GETXVAL functions retrieve values that have been converted into their external form (i.e., run through the element's Outprocs). The form and type of the value retrieved by any of these functions will match the form and type that the element's value has in its unconverted ($GETUVAL, $GETIVAL) or converted ($GETCVAL, $GETXVAL) form.

In a protocol, $GETxVAL provides a way to access element values for a record that has been brought into main memory with the REFERENCE command. These functions can also be used in Uprocs in formats and file definitions. See "Technical Notes," Chapter 11, for more details about the REFERENCE command; online, [EXPLAIN REFERENCE COMMAND.]

The differences between $GETUVAL and $GETIVAL, both of which retrieve the internal form of an element are:

The only difference between $GETCVAL and $GETXVAL is the filtering, as described above.

In sum, what this means is that $GETIVAL and $GETXVAL are generally more useful than the older $GETUVAL and $GETCVAL functions because the new functions take any filtering into effect by default (which is usually what you want) and $GETIVAL handles virtual elements the way you'd expect.

The four functions all have similar argument lists:

  $GETUVAL(elem-name,occ-number,default-value,structural-occ-map,filter)
  $GETCVAL(elem-name,occ-number,default-value,structural-occ-map,filter)

  $GETIVAL(elem-name,occ-number,default-value,structural-occ-map,filter)
  $GETXVAL(elem-name,occ-number,default-value,structural-occ-map,filter)

"Elem-name" is the name of the element you want to access. This is the only required parm; if no other parms are included, the function returns the value of the first occurrence of the named element, or a null value if the element does not occur. To identify non-unique elements by name, use the special naming convention described at the end of this section. [The element named in "elem-name" may come from a phantom structure, with this restriction: you cannot use the "structural-occ-map" parm to navigate to an element in a phantom structure; also, a SPIRES format is more efficient than $GETxVAL for retrieving multiple elements from a phantom structure.]

"Occ-number" specifies which occurrence of "elem-name" you wish to retrieve. The occurrence number is "0" for the first occurrence, "1" for the second, "2" for the third, and so forth; if you do not specify an occurrence, SPIRES will retrieve the first occurrence. The "occ-number" may be affected by the FILTERED or UNFILTERED parm, described further below.

"Default-value" is a value that you would like returned if no value is retrieved from the record. If no value is retrieved and you do not specify a "default-value", the value returned is null.

The complicated "structural-occ-map" is used primarily in file definition Userprocs for traversing nested structures -- formats use indirect frames and protocols use partial processing to accomplish the same task in a more straightforward way. Basically the parm tells which occurrence of each structure is to be used when accessing elements in nested structures; it describes the route along which the record should be traversed to arrive at the specified element.

For example, in a file designed with two nested structures, STRUC1 containing STRUC2, a value of "1.3" tells SPIRES to retrieve an element occurrence only if it lies within the second structural occurrence of STRUC1 and (within that structural occurrence) only if it lies within the fourth structural occurrence of STRUC2. If you code "ANY" for the value, the function will ignore all structural boundaries and retrieve the "occ-number" occurrence of the element, counting all occurrences across all structures. You cannot use the "structural occurrence map" parm on a $GETxVAL function to navigate within nested structures in a phantom structure.

More information about this parm, and about techniques for saving the path navigated using the $GETSPATH or $GETXPATH variable, can be found right after this section [See 7.4.3a.] and in the manual "SPIRES File Definition".

The "filter" parm (whose values, FILTERED or UNFILTERED, can be abbreviated to F or U or any length in between) specifies whether any element filtering you have specified for display with the SET FILTER command should also affect the function when it retrieves an occurrence. If you do not code this parm, the $GETUVAL and $GETCVAL functions ignore any filters that you have set; the $GETXVAL and $GETIVAL functions will pay attention to filters by default.

Important note: the FILTERED parm affects both the "occ" parm and the "structural-occ-map" parm -- for instance, if you were to filter out the first occurrence of an element with the SET FILTER command, a $GETCVAL or $GETUVAL function with an "occ" parm equal to 0 would retrieve the second "real" occurrence as though it were the first occurrence. Element filtering is described in the "Technical Notes" manual, or online, [EXPLAIN FILTERS.]

The following example illustrates a simple use of the $GETXVAL function to retrieve the value of a record-level element:

The first SHOW EVAL command returns the value of the fourth occurrence of the NAME element. The second SHOW EVAL command tries to retrieve the fifth occurrence, but no such occurrence exists, so the default value specified in the function is returned.

In a protocol, you would probably use the $GETxVAL functions in an IF or LET command. For example,

Non-unique Element Names With $GETxVAL

Note that with the first parameter to these functions, "elem-name", if the element named is within a structure and the name is not unique, SPIRES will retrieve values from the first element with that name (the first element encountered in the list shown by the SHOW ELEMENT NAMES command). To specify which element you want in the case of non-unique element names, use the "@" to designate the structural path. This form is common throughout SPIRES in situations where you need a particular structural element. Its syntax looks like this:

where, from left to right, each structure named is nested within the previous one. There must be no blanks around the "@", and you must enclose the entire value in apostrophes, because "@" is a special character. Note that you only need to specify as many structure names from the right as are needed to uniquely identify the desired path.

You can also specify the element you wish to access by using a four-byte hex value representing the "element ID". This value is stored in the $ELEMID variable, which is set each time a GETELEM is executed in a format or an $ELEMTEST or $ELNOTEST function is executed. See the discussion of $ELEMID in the manual "SPIRES Formats" for more information.

(*) $GETCVAL and the $STRUC.OUT Proc

If you attempt to use $GETCVAL or $GETXVAL on an element that is a structure processed by $STRUC.OUT or A33, the function may retrieve a null value if the structure is at the current referenced level; that is, if the structure is at the record level and a $GETCVAL/$GETXVAL is done on that element, the output processing usually done on the values in the structure to concatenate them into a single value may not occur. But if the structure is at a lower level, the values of the elements making up the structure will be retrieved and processed by the $STRUC.OUT proc (or A33 action). The same thing applies to structures being referenced during Partial Processing: if the structure has been referenced or can be specified in a Partial Processing FOR command, the $STRUC.OUT proc may not be executed when a $GETCVAL function is applied to that element, and the function may return a null value.

7.4.3a  (The Structural Occurrence Map with the $GETxVAL Functions)

To access an element from within nested structures, when one or more of the nested structures is multiply-occurring, you may have to tell the $GETxVAL function exactly how to navigate to the particular structure occurrence that contains the element occurrence you want. The fourth argument on the $GETUVAL, $GETCVAL, $GETIVAL and $GETXVAL functions, the "structural occurrence map", lets you do this. It is also used in a similar way with the $GETVOCC function.

If navigation of structures is occurring naturally, such as during the display of a record, and you enter a USERPROC to process an element, $GETxVAL can access any elements within the containing structures, down to and including the record level, without using structural occurrence map.

Here is an example using structural occurrence map:

retrieves the fourth occurrence of the element NAME, but only if it occurs in a structure along the occurrence route described by "1.2.2". Consider the following "show element characteristics" map:

The "1.2.2" map specifies the second occurrence of P1 and, within that structure, the third occurrence of P2 and, within that structure, the third occurrence of P3. Remember, occurrence numbers begin with zero (0). SPIRES will travel along that route, and when it gets to the third occurrence of P3, it will look in that structure for a NAME element and return the fourth occurrence of it, if one exists.

If you do not include the occurrence number as the second argument of the function, SPIRES will by default retrieve the first occurrence of that element within the structure.

It is important to remember that if you code the FILTERED parm as part of a $GETCVAL or $GETUVAL function, any filter information that you have previously established with a SET FILTER command will affect the traversal specified by the structural occurrence map. The $GETIVAL and $GETXVAL functions automatically apply filters. (However, filters can be turned off for those two functions by adding the UNFILTERED parm.)

You can specify a more deeply nested structural hierarchy than necessary; SPIRES will ignore the part of the map that it doesn't need. For example, the fourth occurrence of the NAME element shown above could just as well have been specified as follows:

Once SPIRES has found the requested element along the route described, it goes no further along the specified route.

The structural occurrence map parm on the $GETxVAL functions cannot be used to retrieve elements from phantom structures; you would need to write a customized format to navigate to these "phantom" occurrences.


$GETSPATH and $GETXPATH Variables for Structural Path Information

When the $GETxVAL function is executed, SPIRES remembers the structural occurrence map that it has just navigated to retrieve the element, and saves this information in the $GETSPATH and $GETXPATH variables. [See 6.4.] (SPIRES saves this information whether or not you have used the fourth argument of the function.) Note that the saved information represents an unfiltered path, even if filtering is in effect when the path is traversed. Therefore, the use of a structural occurrence map parameter in a subsequent $GETxVAL function may be incompatible with filtering.

The occurrence map information is available in both string ($GETSPATH) and hex ($GETXPATH) form and may be specified in either form. Using the string form, you could enter the value as "1.2.0.0"; it would be returned by $GETSPATH as '0001.0002.0000.0000'. $GETXPATH would return the same value as '0001000200000000' (the string representation of the 8-byte hex value). The value of both system variables is updated with the next use of either function, so you should save its value off in a variable of your own, if you will want to use it to force retrieval along the same (unfiltered) route later.

Example of $GETCVAL Used With the Structural Occurrence Map

The sample record below may help show how the structural occurrence map relates to a particular record. Note that the structure containing occurrences of PRODUCT.THREE is nested within the structure containing PRODUCT.TWO, which in turn is nested within the structure containing PRODUCT.ONE:

After you bring the record into main memory with the REFERENCE command, you can retrieve particular element occurrences from the record:

Both retrievals are of the 'second' occurrence of the element PRODUCT.THREE, but according to a different structural occurrence map in each case. In this example, the route to the value "Wood Fillers" navigated into the second occurrence of the outermost structure (PRODUCT.ONE) in the record, and then, within that occurrence of PRODUCT.ONE, navigated to the second occurrence of the nested structure (PRODUCT.TWO).

If PRODUCT.THREE was the key of another nested structure, you could have done the following:

You can also specify "ANY" as a value for the fourth argument of the function. This says that you want the nth occurrence of the element, no matter what route it appears on. SPIRES will search each occurrence of each structure until it finds the specified occurrence. Here is an example using the same sample record as above:

After the first command, the default value, '*', is retrieved because there was no fourth occurrence of the PRODUCT.THREE element within the first occurrence of the second structure. But after the second command, the ANY parameter causes the function to navigate straight down into the record, disregarding structural boundaries.

$GETSPATH, $GETXPATH, and Compiled Variables

Because static variables of type HEX are fixed length and $GETXPATH is variable length, if you assign the value of $GETXPATH to a compiled variable, the value is padded to the left with zeroes. This can cause unpredictable results, so it is recommended that you use $GETSPATH and static variables of type STRING when you will be assigning the structural occurrence map information to compiled variables. In cases where the length of the value is predictable and will not change, $GETXPATH offers a slight efficiency gain over $GETSPATH.

7.4.3b  (The $GETVMATCH Function)

---------------------------------------------
Functions: $GETVMATCH
Purpose: Retrieve the number of an element
   occurrence that matches a given string
---------------------------------------------

The $GETVMATCH function compares a given string against element occurrences within a referenced record, stopping when it finds a match and reporting the occurrence number. This function works similarly to the $GETxVAL functions: the argument list is similar, and the method of accessing the data is the same. It is similar in function to the $ARMATCH function, matching a pattern against an array of values.

$GETVMATCH has the following argument list:

where

[More information about this parm, and about techniques for saving the path navigated using the $GETSPATH or $GETXPATH variable, can be found after the section on the $GETXVAL function [See 7.4.3a.] and in the manual "SPIRES File Definition".]

specifies whether any element filtering you have specified for display with the SET FILTER command should also affect the function when it retrieves an occurrence. The allowed values, FILTERED or UNFILTERED, can be abbreviated to F or U or any length in between. FILTERED is the default if you do not code this parm.

Important note: the FILTERED parm affects both the "occ" parm and the "structural-occ-map" parm -- for instance, if you were to filter out the first occurrence of an element with the SET FILTER command, a $GETVMATCH function with an "occ" parm equal to 0 would start at the second "real" occurrence as though it were the first occurrence. Element filtering is described in the "Technical Notes" manual, or online, [EXPLAIN FILTERS.]

The function returns an integer value as follows:

The $GETVMATCH function sets the $GETXPATH and $GETSPATH variables when it returns a positive answer. This helps you determine what path was taken to the occurrences found.

To show some sample uses of $GETVMATCH, we'll assume the following record is referenced:

STUDENT.NUMBER = 226823846;
  QYY = 195;
    COURSE = Etiquette 101: Introduction to Introductions;
    COURSE = Art 115: Late 19th-Century Dutch: The Gogh-Gogh Years;
    COURSE = Lit 203: 3 American Writers: Poe White Nash;
  QYY = 295;
    COURSE = Music 103: Bach's Scores, Low Pitches, Baroquen Plays;
    COURSE = Lit 205: Tennyson, Anyone?

-> show eval $getvmatch(course,0,'Lit?',any)
2
-> show eval $getcval(course,2,'None',any)
Lit 203: 3 American Writers: Poe White Nash
-> show eval $getvmatch(course,3,'Lit?',any)
4
-> show eval $getvmatch(course,0,'Music?')
-1

The reason the last command failed is because without the ANY option (or some other value for the structural-occurrence map), SPIRES looks only at the first occurrence of QYY, so it doesn't get into the 295 structure. In that command, if you add ANY to the function, SPIRES would return the value "3". If you add "1" to the function as the structural-occurrence map, SPIRES would return the value "0", since the Music 103 course is the first (i.e., number 0) occurrence in the requested structure.

7.4.3c  (The $GETVOCC Function)

---------------------------------------------
Functions: $GETVOCC
Purpose: Retrieve the number of element
   occurrences from a referenced record
---------------------------------------------

The $GETVOCC function provides a way to access the number of occurrences of data elements within records. This function works similarly to the $GETxVAL functions: the argument list is similar, and the method of accessing the data is the same.

$GETVOCC has the following argument list:

where "elem-name" is the name of the element to access, "structural-occ-map" is used in the same manner as for the $GETxVAL functions, and "filter-indicator" tells whether or not filtering should be used during the access. FILTERED is the default condition and can be omitted. To turn filtering off, use UNFILTERED.

The function returns an integer value as follows:

The $GETVOCC function sets the $GETXPATH and $GETSPATH variables when it returns a positive answer. This helps you determine what path was taken to the occurrences found.

7.4.4  (The $HEX Function)

------------------------------------------------
Function: $HEX
Purpose: Convert value to type HEX
Related function: $RETYPE
------------------------------------------------

The $HEX function has the following form:

Executing the function converts the "argument" to type hex, unless the form of the argument is incompatible with hex, in which case a conversion error would occur.

7.4.5  (The $INDEXINFO Function)

---------------------------------------------
Function: $INDEXINFO
Purpose: Access index information from
   a file definition
Related functions: $ELEMINFO, $ELIDINFO
---------------------------------------------

The $INDEXINFO function returns a value from an occurrence of an index info-element.

The syntax of the function is:

"Searchterm" is any valid index name available to the user (i.e., indexes hidden by NOSEARCH are not known to this function.) [In case of duplicate searchterms, the first term encountered will be the one used; there is no way to use structural path information to qualify a searchterm for greater precision.] If the index named is not defined, the function returns a null value.

"Info-element" is the name or alias of one of the index info-elements. It designates what information is to be retrieved from the index info-elements. Possible values are:

If the info-element is invalid, a null value is returned.

"Occurrence" is an optional parameter. It is a value, starting from 1, that says what occurrence of the specified info-element is to be accessed. If not given, the first occurrence is assumed. The only index info-elements that are multiply-occurring are DESCRIPTION, SOURCE, EXCLUDE, and USERINFO.

The "default" parameter is also optional, and lets you specify a value to be returned if the requested occurrence of the info-element does not occur.

7.4.5.1  (The $INDEXNUM Function)

---------------------------------------------
Function: $INDEXNUM
Purpose: Retrieve index number from a search
   term for use in $INDEXTERM function
Related functions: $INDEXTERM
---------------------------------------------

The $INDEXNUM function returns an integer (0 or greater) representing the position of the given searchterm in the list of indexes currently available to the user.

The syntax of the function is:

"Searchterm" is any valid simple-index, goal-index or qualifier name available to the user (i.e., indexes hidden by NOSEARCH are not known to this function.) [In case of duplicate searchterms, the first term encountered will be the one used; there is no way to use structural path information to qualify a searchterm for greater precision.]

If the supplied name is not a valid searchterm, or if no subfile is selected, the function returns "0". Otherwise, it returns the ordinal position of the named index in the list of indexes (SHOW INDEXES), counting any qualifiers, and counting compound indexes as one. The "Goal Records" or "Goal -- Index", the first item in the SHOW INDEXES list, is always number 1 in the count.

The most common use of this number is with the $INDEXTERM function, which provides specific information about an index, given its number.

Here is an example session (the SHOW INDEXES display is condensed):

"Credit" returns a zero because it is not a simple index. Compound indexes are internally handled very differently from simple indexes, and the function does not have access to the external searchterms of compound indexes.

7.4.5.2  (The $INDEXTERM Function)

---------------------------------------------
Function: $INDEXTERM
Purpose: Retrieve information about an index
Related functions: $INDEXNUM
---------------------------------------------

The $INDEXTERM function returns information about a given index, sub-index or qualifier, such as its valid searchterms.

The syntax of the function is:

The index-number is an integer representing the index's ordinal position in the list of indexes displayed to the user by the SHOW INDEXES command. The $INDEXNUM function can be useful for retrieving that number for a given index name.

The allowed codes are:

If the supplied number is not valid for an index, or if no subfile is selected, the function returns a null string. [See the description of $INDEXNUM to see how to count the indexes correctly.]

Here is an example session (the SHOW INDEXES display is condensed):

For the compound index, the NAME code makes the function return the internal name coded for the searchterm in the file definition -- it is ID for the compound index in RESTAURANT. It is not a useful value to users. There is no way for the $INDEXTERM function to retrieve the actual searchterms used with a compound index.

7.4.5a  (The $INSERT Function)

----------------------------------------------
Function: $INSERT
Purpose: Insert one string into another string
----------------------------------------------

The $INSERT function lets you insert one string into another.

The function has the following form:

s1 and s2 are string parms, and pos is an integer parm.

The input string, s1, represents the value that you wish to have adjusted, while s2 specifies the insertion string, and pos specifies the position within s1 that you wish to insert s2, counting from the left.

This function inserts s2 into s1 after pos characters, counting from the left of s1. pos may range from 0 thru the size of s1. If pos equals zero, the result is the same as the concatenation: s2||s1. If pos equals the size of s1, the result is the same as: s1||s2. If pos is negative or greater than the size of s1, then s1 is returned unchanged. Likewise, if either s1 or s2 are null strings, s1 is returned.

To insert s2 from the right, simply set pos to the expression "size of s1" minus "right position".

Note that the $INSERT function parallels the $INSERT system proc, with LEFT or FRONT as the "how".

Here is an example of the use of this function:

7.4.6  (The $INSETL, $INSETR, and $INSETC Functions)

----------------------------------------------
Functions: $INSETL, $INSETR, $INSETC
Purpose: Layout a string value in a field
----------------------------------------------

The $INSETL, $INSETR, and $INSETC functions let you layout a value more distinctively by centering the value ($INSETC) or adjusting it to the left ($INSETL) or right ($INSETR) in a specified field, meanwhile surrounding the value with a repeating character such as a period.

The functions have the following form:

The input string in each case represents the value that you wish to have adjusted, while "background-string" specifies the repeating character, and integer3 specifies the total space of the field. (The field can be 256 characters maximum.)

For $INSETL, the input string is left justified in a field integer3 characters wide. If the input string is less than integer3 characters long, then the background-string is used repeatedly to pad the field on the right.

For $INSETR, the input string is right justified in a field integer3 characters wide. If the input string is less than integer3 characters long, then the background-string is used repeatedly to pad the field on the left.

For $INSETC, the value is centered as closely as possible within the field. It is padded on either side with the character specified as the background-string.

If the value is longer than the field, it is truncated on the right for $INSETL and $INSETC, and truncated on the left for $INSETR.

Note that the $INSETL and $INSETR functions parallel the $INSETL and $INSETR system procs, but the procs do not truncate long values.

Here are some examples of uses of these functions: ($key is 09/11/81)

7.4.7  (The $INTEGER Function)

------------------------------------------------
Function: $INTEGER  ($INT)
Purpose: Returns a value converted to type
   INTEGER
Related function: $NEGATIVE
------------------------------------------------

The function $INTEGER (alias $INT) converts its argument into a four-byte integer. The form of the function is as follows:

    $INTEGER(input-value)

"Input-value" must be either an integer or a value convertible to an integer, or a conversion error will occur. [See 5.1.2.] (The argument can range from -2,147,483,647 to 2,147,483,647.)

A null value will convert to a zero, type INT.

Among other possibilities, you can use $INT to ensure that the value you assign to a dynamic variable is of type INTeger:

If there is a chance that the value is not an integer -- for instance, if it might contain decimal places -- you should probably use $PACKED or $REAL instead. For values falling outside the range specified above for integer, use $PACKED.

7.4.8  (The $ISSUECMD Function)

----------------------------------------------
Function: $ISSUECMD  ($ISSUE)
Purpose: Issue a non-SPIRES command (e.g.,
   from a format or USERPROC)
----------------------------------------------

The $ISSUECMD function, alias $ISSUE, sends a command to the underlying system, and returns an integer of 1 if successful, or 0 if the command fails. The function has the following form:

The "command" to be issued may not be a SPIRES command.

Because you can issue subsystem commands from a protocol without using the $ISSUECMD function, the function will be primarily useful in formats or in USERPROCs in file definitions.

Some examples of $ISSUECMD in action:

7.4.8a  (The $ISSUEMSG Function)

----------------------------------------------
Function: $ISSUEMSG
Purpose: Issue a system message
----------------------------------------------

The $ISSUEMSG function issues a SPIRES system message of your design. You can specify its type as Informational, Warning, Serious or Unconditional. Unconditional messages are sent to the currently assigned MSG area (usually the terminal) regardless of the error level set by any SET MESSAGES commands. The other types are controlled by I, W, and S error levels for interactive or XEQ mode, depending on which mode executed the command leading to the $ISSUEMSG function.

Here is the syntax of the $ISSUEMSG function:

where "message" is a string value containing the message to be issued, and "code" is one of the following:

Note that other abbreviations are allowed for the code: any abbreviation down to one character, in fact.

The function normally returns the message string as its value. However, if the string is null, a one-byte blank-character string is returned. If no code or an invalid code is specified, the function returns a null value.

When the message is displayed, it will be preceded by a hyphen, as system messages normally are.

Here are some examples, issued with the EVAL command from interactive mode. Assume SET MESSAGES = 3 is in effect:

7.4.9  (The $LEFTSTR Function)

----------------------------------------------
Function: $LEFTSTR  ($LSTR)
Purpose: Create substring from left portion
   of a string value
Some related functions:  $LEFTSUB, $RIGHTSTR,
   $RIGHTSUB, $SUBSTR, $XSTR
----------------------------------------------

The $LEFTSTR or $LSTR function truncates an input string, returning a substring consisting of a certain number of characters from the leftmost portion of the input string. The function takes the form:

The "integer" argument determines how many characters of the input string will be returned as a substring.

For example:

7.5.1  (The $LEFTSUB Function)

-------------------------------------------------
Function: $LEFTSUB  ($LSUB)
Purpose: Return a substring truncated to the left
   of a specified substring
Related functions: $RIGHTSUB, $LEFTSTR
   $RIGHTSTR, $SUBSTR, $XSTR
-------------------------------------------------

The $LEFTSUB or $LSUB function truncates a value to its leftmost or beginning portion.

The function has the form

If string2 is a substring of the input string, then $LEFTSUB returns all of the input string up to (but not including) the first character of string2. [If argument 2 is null or not contained by argument 1, then the value returned is null.]

Here are a few examples of $LEFTSUB:

Another example:

I.e., if the value for $TIME were 16:32:04, then HOUR would be 16 and PERIOD would be DAY.

7.5.2  (The $LINE Function)

--------------------------------------
Function: $LINE
Purpose: Convert value to type LINE
--------------------------------------

$LINE is a single-argument function that converts its input value to type LINE. It is a single argument function of the form:

$LINE can be used to help ensure that a value being compared to a line variable such as $WDST represents a valid value for a line.

7.5.3  (The $LOG Function)

------------------------------------------
Function: $LOG
Purpose: Compute logarithm of value
Related functions: $EXP, $MOD, $RANDOM,
   $SQRT, $TRUNC
------------------------------------------

$LOG is a single argument function of the form:

"Real-value" is any value that either is of type REAL (i.e., floating-point) or that can be successfully converted to type REAL. $LOG returns the Naperian logarithm (base "e") of its argument. (The returned value is also of type REAL.)

If the argument is zero or negative, $LOG returns a zero result.

Some examples follow:

7.5.4  (The $LOOKSUBF and $LOOKSUBG Functions)

 ----------------------------------------------
 Functions: $LOOKSUBF, $LOOKSUBG
 Purpose: Access records from other subfiles
    or record-types
 ----------------------------------------------

The $LOOKSUBF and $LOOKSUBG functions let you access records from other subfiles or record-types. ($LOOKSUBF is most commonly used when you have another subfile selected, but in fact, it can be used whether or not a subfile is currently selected.) SPIRES will look in the specified subfile or record-type for a record having a key equal to the value you have specified in the function. At that point, depending on the other values you have specified in the function's argument-list, the function either verifies that such a record exists, or returns a count of the number of occurrences of an element, or returns the value of an element in that record. The element's value can be returned in either its internal or external form.

The syntax is:

The parameters can have the following values:

Below are some examples:

SPIRES will look in the subfile named ORDERS for a record with the integer key "123". If a record is found, the function will return a 1; if no record is found, it will return a 0.

SPIRES will look for record 123 in the subfile ORDERS and return the value of the element DESC. If no record is accessed, or no value exists for DESC, the function will return "none".

SPIRES will look in the record-type named REC04, which must be defined as a subgoal for the currently selected subfile, and verify the existence of a record with the integer key "123".

SPIRES will look in the record-type named INDX7, which must be defined as a subgoal for the currently selected subfile, and count the occurrences of the PTRG element in the record with the "PHYSICS" key. If this is a LARGE SEGMENT record, the entire large segment chain will be traversed to return the total count of PTRG elements across all segments.

If you include REPLACE in the function to access the internal form of the element, and that element has OUTPROC-REQ coded for it in the file definition, the function will return a value as if no value for the element existed. (i.e., a null or the value coded in the DEFAULT parameter).

7.5.4a  (The $LOOKSYS Function)

 ----------------------------------------------
 Functions: $LOOKSYS
 Purpose: Retrieve record data from system
    subfiles
 Related function: $LOOKSUBF
 ----------------------------------------------

This function, similar to $LOOKSUBF, provides access to records in several system subfiles. You provide: a code identifying the system subfile you want to use; the key of the record you're interested in; and the name of the element whose value you want returned for the record you named.

Currently it is useful only in Prism for use with Prism meta-data.

Its syntax is:

The record-key will always be converted to STRING and passed through the INPROC rule for the key of the target subfile before retrieval takes place. A null value for the record-key is not allowed, and causes an error. The returned value, if it exists, will always be in its external form. If it doesn't exist, a null value will be returned by the function.

$LOOKSYS translates to $LOOKSUBF in the following way:

7.5.5  (The $MATCH Function)

---------------------------------------------------
Function: $MATCH
Purpose: Compare a value against a predetermined
   list of string patterns
Related functions: $PMATCH, $AMATCH, $APMATCH
---------------------------------------------------

The $MATCH function matches an input string against an argument list of string patterns. The form of the function is as follows:

The first argument names an input string (explicitly or by naming a variable) against which one or more patterns are to be matched. The patterns are given in the second and succeeding arguments. [The number of arguments allowed in the $MATCH function can vary -- you can count on roughly twenty patterns being allowed. If you need a larger number than this, you might consider storing the patterns in an array and using $AMATCH or $APMATCH.]

The patterns are strings, which may begin and/or end with a '?'. If a pattern begins with a '?', then anything may precede the pattern string in the input string:

If a pattern ends with a '?', then anything may follow the pattern string in the input string:

If a pattern both begins and ends with a '?', then anything may precede or follow the pattern string in the input string:

If a pattern has a '?' embedded in it, the '?' matches any or no characters in the input string:

Any character string containing embedded blanks must be enclosed in quotes.

The $MATCH function returns the integer number of the first pattern string in which a match succeeded. If no match is found, 0 is returned.

$MATCH and Case

As shown by the last example, case is significant in the specification of $MATCH arguments. If the input string, argument 1, is $ASK, it may be helpful to specify the case of the ASK command locally, using ASK UPPER. [See 5.3.] You can also use the $CAPITALIZE or $CASE function. For example:

7.5.6  (The $MOD Function)

-----------------------------------------------------
Function: $MOD
Purpose: Find remainder of division between integers
-----------------------------------------------------

$MOD is a two-argument function in this form:

where both arguments must either be of type integer or be convertible to type integer. $MOD returns the remainder from the division of the first argument by the second. If the second argument is "0", then "0" is returned.

For example,

If both arguments have the same sign (positive or negative), then the result is simply the remainder from the integer division. However, if they have different signs, then the second argument, the divisor, is added to the remainder from the integer division.

For example,

7.5.7  (The $NEGATIVE Function)

------------------------------------------------
Function: $NEGATIVE  ($NEG)
Purpose: Convert value to type INTEGER and
   change the value's sign
Related function: $INTEGER
------------------------------------------------

$NEGATIVE (or $NEG) is a one-argument function of the following form:

where "input-value" must be an integer or convertible to an integer.

$NEGATIVE changes the sign of its value, from positive to negative or vice-versa:

     -> let x = $negative(3)
     -> let y = $negative(-4)
     -> show eval #x ' ' $type(#x)
     -3 INT
     -> show eval #y ' ' $type(#y)
     4 INT

The input value for $NEGATIVE (as for $INTEGER) must be within -2,147,483,647 and 2,147,483,647 or a conversion error will occur.

For arithmetic with decimal values, use $REAL or $PACKED instead.

7.5.8  (The $NOLF Function)

----------------------------------------------
Function: $NOLF
Purpose: Write a string to the terminal,
   suppressing the final line feed and
   carriage return
----------------------------------------------

The $NOLF function lets you write a line to the terminal without a carriage-return and line feed being appended to it. The next line written to the terminal will be on the same line as the last.

The function has the form:

where "input-string" is the string (or message) to be written to the terminal. The function is nearly always used with the EVAL command, e.g., EVAL $NOLF('input-string'). If you specify the null string (i.e, ''), the terminal will produce an "idle" character (the print head or cursor will jump, then return to its starting location).

7.5.8b  (The $NORMALIZE Function)

------------------------------------------------
Function: $NORMALIZE function
Purpose: Returns a normalized packed decimal
   value as a string
------------------------------------------------

The form of the function is as follows:

    $NORMALIZE(packed-value)

$NORMALIZE takes a packed-value, or any value that converts to packed, and returns a normalized string suitable for $PACK. A "normalized" value has only three basic forms:

    1.  Pure integer, like:  2315
    2.  Exponential,  like:  1.234E5  or  8.712E_9  or  8E29
    3.  Mixed number, like:  7.128   (one digit before decimal point)

Here are examples of each form:

    1.  $normalize('12.334E3')  ->  12334
    2.  $normalize('12.334E_2') ->  1.2334E_1
    3.  $normalize('12.334E_1') ->  1.2334

If you intend to do numeric comparisons of the result, you should probably apply $PACK to the result.

7.5.9  (The $PACKED Function)

-----------------------------------------
Function: $PACKED  ($PACK)
Purpose: Return a value converted to type
   PACK
-----------------------------------------

The $PACKED (or $PACK) function converts the argument to a packed decimal value. The function's form is as follows:

    $PACKED(input-value)

"Input-value" is any value that can be successfully converted to a packed data type. (Numeric values such as integers can successfully convert to packed, though the reverse is not necessarily true.)

A packed decimal allows SPIRES to handle very large numbers properly, whether they have decimal places or not. SPIRES can perform arithmetic operations with packed decimal values having up to 29 places of precision (the number of digits beginning with the left-most non-zero digit through to the right-most digit).

Exponential notation allows the value to be multiplied by as much as 10 to the 127th power, or divided by as much as 10 to the 128th power. The converted value will have such packed decimal characteristics as precision and magnitude, which are used in some of the other functions relating to packed decimals.

Input values outside of the allowed ranges return "@@" (positive infinity), "0" (zero) or "-@@" (negative infinity). A null input value will be converted to a zero.

Packed decimal value handling is described in detail in chapter 18 of the manual "SPIRES Technical Notes". Online, issue the command EXPLAIN PACKED DECIMALS for more information.

7.6.1  (The $PACKTEST Function)

---------------------------------------------
Function: $PACKTEST
Purpose: Query a packed decimal value
Related functions: $DECIMAL, $PRECISION
---------------------------------------------

The $PACKTEST function returns information about a packed decimal value. Its syntax is:

where "value" is a packed decimal value (or a value that can be converted to packed) and "code" is one of the following:

(The codes may be abbreviated to their first character.) The result is always an integer value. [EXPLAIN PACKED DECIMALS, PRECISION.] for information about these concepts in relation to packed decimal values, or see the manual "SPIRES Technical Notes".

Here are some examples of the $PACKTEST function:

As demonstrated by the above example, the value returned by DECIMAL is the negative of the value returned by EXPONENT.

7.6.2  (The $PARSE and $PARSESTRIP Functions)

----------------------------------------------
Function: $PARSE
Purpose: Break input value at a point to the
   left of a character in a second string
Related functions: $BREAK, $SPAN, $STRIP, $PARSESTRIP

Function: $PARSESTRIP
Purpose: Remove container characters and
   "undouble" characters in $PARSEd value
Related functions: $PARSE, $DOUBLE
----------------------------------------------

$PARSE is a three-argument function somewhat similar to (though more complicated than) $BREAK. The function's form is as follows:

The arguments in the argument list are all required:

$PARSE searches the input string until it finds there one of the characters in string2 that is NOT in a "containing group" (i.e., NOT surrounded by one of the pairs of characters named in "string3"). When $PARSE finds such a character it stops scanning the input string any further and returns the characters of the input string, up to but not including the character that stopped the scan. (If no character stops the scan, all of the input string is returned.)

$PARSE is thus different from $BREAK in that it prevents a break character within a containing group from causing a break; $BREAK does not recognize containing groups.

$PARSESTRIP is used to strip off the container character at the front and back of a $PARSEd value (if any) and, if the input value has container characters that are doubled inside the front and back container characters, those doubled characters will be undoubled. So, for instance, if $PARSE returns:

where the quotation marks are container characters, then $PARSESTRIP applied to it would return:

The syntax of $PARSESTRIP is:

where "input-string" is the string value returned from $PARSE, and "string3" is the same string defined as "string3" in $PARSE, that is, the characters that begin and end containing groups.

Containing Groups

As stated above, the paired characters in "string3" determine what comprises a containing group. If both characters in a pair are the same (e.g., "%%"), they define a "delimiter group". If they are different (e.g., "{}") they define a "nesting group". Delimiter groups may not be nested.

If string3 has an odd number of characters, the last (odd) character is ignored. Any character used in one pair cannot be used in another pair. If a character is used again in a second or subsequent containment pair, or if a character in string2 is used in a containment pair, that pair is ignored. By default, if string3 is null, apostrophes and quotation marks are used to define delimiter groups (though their appearance in string2 would override the default). String3 must be included in the function, so to be "null", it must be specified as a pair of apostrophes or quotation marks.

Suppose you are picking off the individual tokens of an options list:

You set up a loop that first parses the value at blanks, unless the blanks are within allowed containers, which in this case are apostrophes or quotation marks:

$PARSE is thus looking for the first blank in the value that is not within a containing group, which is defined by default as being surrounded by either apostrophes or quotation marks. Hence, TITLE would be returned as the value of the function, as the first token.

Your loop would then strip off the first token (e.g., using $RSUB), and start again. The second time:

Note that the apostrophes, the container characters, are part of the returned value. If you did not want them, or if the contained value itself includes a container character (doubled), then $PARSESTRIP would come in handy:

$PARSE and the $SNUM System Variable

$PARSE sets the system variable $SNUM to 0 if the scan does not encounter errors. Otherwise, $SNUM is set to one of the following error numbers:

Example of Error

  -> let x = '(3+5)) + (2*10)'       <--note unbalanced parentheses
  -> let parsed = $PARSE(#x,',','""''''()')
  -> show eval #parsed
  (3+5)
  -> show eval $snum
  772

(The delimiters for string3 are hard to read in this example, but consist of a pair of quotation marks, a pair of apostrophes, and a set of parentheses.)

7.6.2a  (The $PATHFIND and $PATHINFO Functions)

-------------------------------------------------
Functions: $PATHFIND, $PATHINFO
Purpose: Obtain information about a path or about
   the subfile (format, etc.) open on that path
-------------------------------------------------

The two functions $PATHFIND and $PATHINFO retrieve information about a path that has been opened as part of your application's processing -- or about the subfile that is selected (or the format or vgroup that is set) on that path. [For more information about path processing in general, see the manual "SPIRES Technical Notes".]

The $PATHFIND Function

The $PATHFIND function returns the integer path number of a path that has been specified by path-name or subfile-name. The function's form is as follows:

   $PATHFIND(path-string,code)

"Path-string" is either the name of the opened path or else the name of the subfile selected on that path. If it is longer than one word, the subfile name should be placed in quotes; it may also be fully qualified (EXPLAIN SUBFILE NAMES, NON-UNIQUE for details) or it may be a temporary subfile name.

The value for "code" tells SPIRES what your "path-string" value represents:

  Code     Purpose
  -------  ------------------------------------------------------
  SUBFILE  tells SPIRES that the "path-string" is the name of
           a subfile.
  PATH     tells SPIRES that the "path-string" is the name of the
           path itself.

(These values for "code" can be abbreviated to three letters.)

If $PATHFIND does not find a path corresponding to the input values you specify, the function returns a value of -1. Otherwise, it returns a value of 0 for the primary path, and 1 through n for other paths.

For example:

  -> select bookstore
  -> thru path1 select formats
  -Path established: 1 PATH1
  -> thru path2 select filedef
  -Path established: 2 PATH2
  -> show eval $pathfind('bookstore',SUBFILE)
  0
  -> show eval $pathfind('filedef',SUBFILE)
  2
  -> show eval $pathfind('path1',PATH)
  1

The $PATHINFO Function

The $PATHINFO function returns a string representing information for a path that you have specified by path-number. The function's form is as follows:

  $PATHINFO(path-number,code)

"Path-number" is the number of the path for which you want information, counting from 0 for the primary path.

"Code" currently has these possible values, which can all be abbreviated to three letters:

  Code     Purpose
  -------  --------------------------------------------------------
  NAME     returns the path's name, if there is one.  (Otherwise
           it returns a null string.)
  TYPE     returns the path's type: possible values are ATTACH,
           SUBFILE, FORMAT, or VGROUP.
  SUBFILE  returns the name of the subfile, if any, that was
           selected on the path.

For example:

  -> select bookstore
  -> thru formpath set format bookread
  -Path Established: 1 FORMPATH
  -> show eval $pathinfo(1,TYPE)
  FORMAT
  -> show eval $pathinfo(1,NAME)
  FORMPATH
  -> thru newpath clear vgroups
  -Path established: 2 NEWPATH
  -> show eval $pathinfo(2,TYPE)
  VGROUP

$PATHINFO returns a null string if the requested value is null or if the information you've requested simply doesn't exist for that path.

(*) $PATHINFO and ATTACH

Although the ATTACH command can't be issued on a path, you might use $PATHINFO to determine whether the primary path represents an attached record-type or a subfile select.

  -> attach 2 of gq.abc.books
  -> show eval $pathinfo(0,TYPE)
  ATTACH

7.6.3  (The $PMATCH Function)

---------------------------------------------------
Function: $PMATCH
Purpose: Match a value against a pattern or stem
Related functions: $AMATCH, $APMATCH, $MATCH
---------------------------------------------------

The $PMATCH function matches an input string against one or more string patterns, returning a number corresponding to the number of the pattern that matched the input string. $PMATCH operates similarly to the $MATCH function, but is more useful and efficient than $MATCH for matching by an exact stem, as demonstrated below.

The form of $PMATCH is as follows:

where:

$PMATCH attemps to match its input string against each of the patterns and returns an integer "n" corresponding to the "nth" pattern, the one that has satisfied the match requirements: [The matching of the input string to the patterns is attempted through the length of the input string. Thus, if an input string of three characters matches the first three characters of a pattern, the function is satisfied; any additional characters that the pattern might have (e.g., a fourth or fifth character) would be disregarded. (A pattern shorter than the input string would be skipped altogether.)]

If $PMATCH cannot find a match, it returns a value of "0" (zero).

$PMATCH and Case

Case is significant for matching with $PMATCH:

   (1)  $PMATCH(Y, YES, OK, NO) = 1
   (2)  $PMATCH(yes, YES, OK, NO) = 0

In example (1) the input string "Y" matches "YES" through the length of the input string. ("YE" or "YES" as input strings would also return "1" but a input string of "YESIREE" or "YEP" would return "0".) In example (2), however, the lower case input string "yes" does not match the upper case pattern string "YES", so $PMATCH returns a "0" value. The $CASE or $CAPITALIZE function can alleviate this problem:

Matching by Exact Stem With $PMATCH

If you want to ensure that no match is made unless the input string matches a certain minimum number of characters in a pattern, the pattern should have a question mark following the minimum desired length.

The following example enforces a minimum length of three characters for the stem:

If #X = "D" or "DI", no match is made ("0" is returned), since the "minimum" value of #X for a match is "DIS" or "TRA". If #X = "DIS" or "DISP" or "DISPLA", the function would return the integer "2". (Don't forget to capitalize the value before matching it with upper case patterns.)


7.6.4  (The $PRECISION and $WINDOW Functions)

---------------------------------------------
Functions: $PRECISION and $WINDOW
Purpose: Change precision of a packed decimal
            value
Related functions: $DECIMAL, $PACKTEST
---------------------------------------------

The $PRECISION and $WINDOW functions are used to change the "precision" of a numeric value, where precision is defined as the number of digits in the value counting from the left-most non-zero digit to the right-most digit.

The syntax is:

where:

The $PRECISION function can add or subtract precision; the $WINDOW function will only subtract precision, but is otherwise the same as $PRECISION.

Here are some sample values as handled by $PRECISION:

     Input value     Precision      Result
     -------------   -----------    -------
(1)    3               4            3.000
(2)    123.45          4            123.5
(3)    0.012345        4            0.01235
(4)    12345           4            1234E1
(5)    9999.56         4            10000

In example (1), zeroes are appended to create four places of precision in the value. (The same value processed by $WINDOW would have been unchanged, since $WINDOW does not add precision.) In example (2), the input value has five places of precision, so appropriate rounding is applied, and the value is reduced to four places of precision. In example (3), note that the result has only four places of precision, since precision is counted from the left-most non-zero digit in the value.

In example (4), the result is displayed in exponential notation because the result "12340" would contain five places of precision, which is more than was requested. In example (5), the result actually has five places of precision, due to rounding. This unusual occurrence can only happen when rounding raises a value to a power of ten (e.g., 10, 10000, 0.01).

For more information about the precision of packed decimal values, see the manual "SPIRES Technical Notes" or online EXPLAIN PRECISION OF PACKED DECIMALS.

7.6.4a  (The $PRISMINFO Function)

---------------------------------------------
Function: $PRISMINFO
Purpose: Retrieve information about current
   Prism environment
---------------------------------------------

The $PRISMINFO function returns information to applications about the current Prism environment. It packages information formerly available only by defining new system variables or Prism variables.

Currently, the function returns three types of information: information about the current search, information about the current sort, and information about the current Prism version.

The syntax of the function is:

For SEARCH Information

Search-parm" can be one of the following:

If there is no current search, then all of the SEARCH variants of the function return null.

The third parm (EXPANDED|TYPED) is reserved for a planned extension to the function.

For SORT Information

"Sort-parm" can be one of the following:

This indicates that, for the user's current SORT command in Prism, the SPIRES sequencing was on FORM.NAME and FORM.ID elements.

If there is no current sort specified by the user, then all of the SORT variants of the function return null.

For VERSION Information

The VERSION variant of the function returns information about the current version of Prism (and SPIRES, if the LONG parm is used).

For SHORT, the standard values are:

7.6.5  (The $PROCSUBG Function)

------------------------------------------------
Function: $PROCSUBG
Purpose: Process input value through inprocs or
   outprocs for element in selected subfile's
   record-types
------------------------------------------------

The $PROCSUBG function lets you run any value through the inprocs or outprocs of an element in any record-type (or subgoal) of your currently selected subfile. [For a fuller description of inprocs and outprocs, see the manual "SPIRES System Procs".]

The syntax is:

The parameters, all of them required, can have the following values:

Here is an example of $PROCSUBG running a dollar-type amount through the inprocs for a dollar-type element in the selected subfile:

The function looks in REC01 of the currently selected subfile for inprocs on the AMOUNT element and applies them to the value "$54.00".

7.6.6  (The $RANDOM Function)

-------------------------------------------
Function: $RANDOM
Purpose: Generate a random number
-------------------------------------------

$RANDOM is a one-argument function that generates a random number. The form of the function is as follows:

where "n" is a positive integer. (If n is negative, the function uses its absolute value.)

The $RANDOM function returns an integer in the range 0 to n-1, as the following example suggests:

The initial "seed" from which numbers are derived is an internal clock, but you can use the SET SEED command (described below) to insure that $RANDOM always generates the same sequence of random numbers. The $SEED variable contains the current seed value.

The SET SEED Command for Random Number Generation

The SET SEED command can be used in cases where you want to generate the same sequence of random numbers more than once. The form of the command is

where "n" is a positive integer or zero (0). The value of the $SEED variable now contains this number.

For example,

A given seed value would always generate these same random values.

7.6.7  (The $REAL Function)

-------------------------------------------
Function: $REAL
Purpose: Convert value to type REAL (i.e.,
   floating point)
Related functions: $INTEGER, $LINE,
   $PACKED
-------------------------------------------

The $REAL function has the following form:

When the function is executed, the argument is converted to a real number. If the conversion is impossible, a conversion error results.

Among other possibilities, $REAL can be used to guarantee that a dynamic variable is assigned a value of type REAL.

A null value for the argument will convert to a zero, type REAL.

7.6.7a  (The $RECINFO Function)

-----------------------------------------
Function: $RECINFO
Purpose: Return various types of
   information about a record
Related function: $RECTEST
-----------------------------------------

$RECINFO is a two-argument function that can be used to determine the actual storage size in bytes of a record in the selected subfile (or accessed record-type). The function can also return the four-byte hex locator for the record if it is removed to the residual.

The function's form is as follows:

where

For $RECINFO(key,SIZE)

If the value returned by the function is a positive integer, that is the size of the record in bytes. If no record with that key is found, the returned value is zero. If an error occurs during the processing of this function, the returned value will be a negative integer.

The size is the same value that would be returned by the SHOW RECORD OVERHEAD command; however, unlike that command, restricted to users with See access to the entire file, this function is available to any user of the subfile.

For $RECINFO(key,LOCATOR)

SPIRES returns a 4-byte hex value representing the locator in the residual of the record. If the record doesn't exist, an integer value of 0 (zero) is returned. If the record exists but the record-type is not Removed to the residual, then a value of 2 is returned. If the record exists in the deferred queue and the residual locator has not yet been assigned, the returned value is 1 (one).

7.6.8  (The $RECTEST Function)

-----------------------------------------
Function: $RECTEST
Purpose: Test whether input value is key
   of existing record and, if so, return
   status of record
Related function: $DEFQTEST
-----------------------------------------

$RECTEST is a one-argument function that can be used to determine whether a record with a particular key exists in the currently selected subfile (or accessed record-type), and if so, whether the record has been added, updated or removed since the file was last processed.

The function's form is as follows:

where

If the value returned by the function is a positive integer, the record exists in the subfile. If the value is less than or equal to zero, the record does not exist or an error occurred during the processing. In particular, if $RECTEST

Note that, if necessary, the input value may be driven through the inproc rules for the key.

7.6.8a  (The $REF Function)

-----------------------------------------
Function: $REF
Purpose: assign a static variable to
   another static variable defined as a
   reference variable
Related functions: $SETPARMS, $GETPARMS
-----------------------------------------

$REF is a one-argument function that connects a static variable or array to a reference variable or array. In essence, that means that anytime the reference variable is used, the current value of the variable it references will be used. This is particularly useful with the $SETPARMS and $GETPARMS functions; variables can then be shared between a program and subroutines that don't even know the name of the variable in the main program.

The function's form is as follows:

where

If the READONLY parameter is included, then you cannot change the value of the reference variable; if it is omitted, you can, and that change will also be reflected in the variable it references. [See 4.5.2 for more information on $REF usage.]

7.6.9  (The $REMAINDER Function)

---------------------------------------------
Function: $REMAINDER
Purpose: Return remainder of packed decimal
   division
---------------------------------------------

The $REMAINDER function returns the remainder from the division operation of one packed decimal value by another. [See the manual "SPIRES Technical Notes" for information on packed decimals in SPIRES.]

The function's form is as follows:

"Packed-dividend" is the number being divided and "packed-divisor" the number performing the division. Both of these input values must be packed decimal values or convertible to packed decimal values (e.g., integer or real). The value returned will also be a packed decimal value.

Note that the two input values are separated by a comma, not a division sign -- e.g., $REMAINDER(1/3) is invalid syntax.

The division is performed to the maximum precision possible. For example, $REMAINDER(70,4) returns zero, because 70 can be divided by 4 with no remainder, i.e., 1.75. However, $REMAINDER(1/3) would return the packed value 1E_27 (1 times 10 to the minus 27th power), because the remainder is still non-zero at that level of precision.

If you are looking for a function to return the remainder of integer division, see $MOD.


7.7.0  (The $RESINFO Function)

------------------------------------------------
Function: $RESINFO
Purpose: Return information about searches
   as maintained by result history
Related functions: $SEARCHINFO
------------------------------------------------

The $RESINFO function provides an easy way to retrieve information about searches the user has done when result history is turned on (using the SET RESULT HISTORY command).

The function's syntax is:

where:

For PREVIOUS and RESULT, the function returns a "-1" value if the requested result history item is invalid, doesn't exist or encounters errors. For COMMAND, the function returns a null string in those cases.

For example, if you have the following result history:

you would get the following data returned from various $RESINFO function requests:

7.7.1  (The $RETYPE Function)

------------------------------------------------
Function: $RETYPE
Purpose: Return a new value based on the
   input value but with redefined data type
Related functions: $TYPE, $TYPETEST
------------------------------------------------

The $RETYPE function offers a way to return the input value with a redefined data type. The function's form is as follows:

where:

The $RETYPE function returns the redefined value of the input value, without changing the input value itself.

For example:

    -> let x = XYZ
    -> show eval #X ' redefined as hex returns ' $retype(#X,hex)
    XYZ redefined as hex returns E7E8E9

$RETYPE can be useful for causing a value to be treated as having a particular data type, when you do not wish actually to convert it to that data type and risk changing the meaning of the value. For a detailed description of how $RETYPE can be useful in Userprocs, EXPLAIN SET VALUE UPROC, TYPE CONVERSION online or see the description of the $PROCVALUE variable in the manual "SPIRES File Definition".

A similar feature for redefining values of static variables, the REDEFINES statement, was described earlier in the section on vgroup definitions. [See 4.1.1.]

7.7.1b  (The $REVERSE Function)

-------------------------------------------------
Function: $REVERSE
Purpose: Reverse characters in input string
           front to back
-------------------------------------------------

The $REVERSE function turns the input string around, so that the first character is last, and the last is first.

The $REVERSE function's syntax is:

where "input-string" is the string to reverse.

The function returns the reversed string.

Here are a couple examples of its use:

The function will probably be most useful in cases where you need to work with pieces of a value from right to left, but the functions you might want to use, such as $BREAK or $PARSE, work on values from left to right. You could first reverse the value with $REVERSE, pick off the last (now first) piece with the other function, and then again reverse that piece to put it in its normal orientation.

7.7.2  (The $RIGHTSTR Function)

----------------------------------------------
Function: $RIGHTSTR  ($RSTR)
Purpose: Create substring from right portion
   of a string value
Related functions: $LEFTSTR, $LEFTSUB,
   $RIGHTSUB, $STRIP, $SUBSTR, $XSTR
----------------------------------------------

The two-argument $RIGHTSTR function, alias $RSTR, lets you truncate an input string by the number of characters you specify in the second argument of the function, returning the right-hand portion of the input string. For example, if the current value of $DATE is 10/01/87, $RIGHTSTR($DATE,6) truncates the first six characters and returns the two rightmost characters:

  -> show eval $rightstr($date,6)
  87

The function's form is as follows:

The "integer" argument determines the number of characters to be truncated from the input string, starting at the leftmost portion of the string.

Note that $LEFTSTR and $RIGHTSTR are complementary. The $LEFTSTR function returns the first n characters and $RIGHTSTR returns the characters to the right of the nth character. Thus, using the example above, a concatenation of $LEFTSTR and $RIGHTSTR returns the original input string.

7.7.3  (The $RIGHTSUB Function)

----------------------------------------------
Function: $RIGHTSUB  ($RSUB)
Purpose: Create substring from right portion
   of a string value
Related functions: $LEFTSTR, $LEFTSUB,
   $RIGHTSTR, $STRIP, $SUBSTR, $XSTR
----------------------------------------------

The $RIGHTSUB or $RSUB function returns a truncated-from-the-front version of the input string, returning everything to the right of a substring. The function takes the form:

The value returned will be all of "input-string" to the right of "string2". Scanning for string2 is done from left to right, and scanning stops with the first occurrence of "string2" found.

If string2 does not occur in the input string, then $RIGHTSUB will return a null value. If string2 is null, then $RIGHTSUB will return the input string.

Below are examples of $RSUB:

Both $LEFTSUB and $RIGHTSUB discard the located "string2" part of the input string (e.g., there is no blank space before UPDATE in the result shown above).

7.7.3a  (The $RMATCH Function)

---------------------------------------------------
Function: $RMATCH
Purpose: Compare a value against a predetermined
   list of string patterns
Related functions: $MATCH, $PMATCH, $AMATCH,
   $APMATCH, $ARMATCH
---------------------------------------------------

Like the $MATCH function, the $RMATCH function matches an input string against an argument list of string patterns. The form of the function is as follows:

The first argument names an input pattern (explicitly or by naming a variable) against which one or more strings are to be matched. The strings are given in the second and succeeding arguments. [The number of arguments allowed in the $RMATCH function can vary -- you can count on roughly twenty strings being allowed. If you need a larger number than this, you might consider storing the patterns in an array and using $ARMATCH.]

$RMATCH is the "Reverse" of $MATCH, in that the input value is specified as a pattern that is matched against strings. This is sometimes easier to use than $MATCH and $PMATCH when you have an input value that is a string that is matched against patterns.

The pattern is a string, which may begin and/or end with a '?'. If a pattern begins with a '?', then anything may precede the pattern string in the matching string.

If a pattern ends with a '?', then anything may follow the pattern string in the input string:

If a pattern both begins and ends with a '?', then anything may precede or follow the pattern string in the input string:

If a pattern has a '?' embedded in it, the '?' matches any or no characters in the input string:

Any character string containing embedded blanks must be enclosed in quotes.

The $RMATCH function returns the integer number of the first pattern string in which a match succeeded. If no match is found, 0 is returned.

As with $MATCH, case is significant.

7.7.3b  (The $ROOT Function)

----------------------------------------------
Functions: $ROOT
Purpose: Find the n-th root of a packed value
----------------------------------------------

$ROOT takes a packed-type, or any value that converts to a packed, as the first parameter, and an unsigned integer greater than 1 as the second operand. The second operand is the "root" to be obtained for the first operand. For even roots, the first operand must NOT be negative. For odd roots, the first operand can be any packed-type value. If "root" is 2, you get the square-root. If root is 3, you get the cube-root, etc. The practical limit for "root" is about 100. The result returned is the string form of a normalized number or mixed-number (like nnnn.fffff). You can supply "infinity" as the first parameter to $ROOT without error, unless it is -@@ for an even root.

Here is an interesting example using $ROOT and $NORMALIZE: [See 7.5.8b.]

The volume of the earth contains 1.33E50 atoms. The value for "pi" can be given by the ratio: $pack(355) / 113. The formula for VOLUME of a sphere is given by: V = 4/3 pi R^3. The RADIUS is therefore: R = $ROOT( V * 3 / 4 / pi). The SURFACE AREA is related to both V and R by the formula: S = V * 3 / R. So to find the surface area of the earth in numbers of atoms, we compute:

    let V = $pack('1.33E50')
    let PI = $pack(355) / 113
    let S = $normalize( #V * 3 / $root( #V * 3 / 4 / #PI, 3) )
    S = 1.260042607478637474E34

The number of IPV6 Internet Addresses is given by:

    let IPV6 = $normalize( $exp( 2, 120) )
    IPV6 = 1.32922799578491869525565033784E36

So every atom on the surface of the earth could be assigned an IPV6 address and we would still have enough left over for more than 100 other earths!

7.7.3c  (The $RSTRIP and $LSTRIP Functions)

----------------------------------------------
Functions: $LSTRIP and $RSTRIP
Purpose: Strip away leading/trailing strings
Related functions: $STRIP
----------------------------------------------

These functions try to remove the Sub_string from either the Left or Right end of the Input_string. The functions continue to do that until the Sub_string can't be removed. If the Sub_string is just a single character, then $LSTRIP acts like $STRIP for a single character strip. Examples:

The functions can reduce the result to null if the Input_string consists of a repeating pattern of the given Sub_string. Typically, $RSTRIP is used to strip one or more of a particular character from the Right end of the Input_string, such as strip trailing blanks: $RSTRIP(Input_string, ' ')

7.7.4  (The $SEARCHINFO Function)

----------------------------------------
Function: $SEARCHINFO
Purpose: Return search commands issued
   for the current search
----------------------------------------

The $SEARCHINFO function is a two-argument function that takes the form:

where "code" in fact has only one feasible value: COMMAND. (You can abbreviate COMMAND to COM.)

The $SEARCHINFO returns the previously-issued search command corresponding to the integer named in parentheses after the function. For example, $SEARCHINFO(2,command) would return the second search command you have issued in your current search, $SEARCHINFO(4,com) would return the fourth search command issued in your current search, and so on.

Using $SEARCHINFO, you can retrieve any legal search command you have issued within your selected subfile since your last FIND command. (Compare this with the SHOW SEARCH command.) Here is an example using the Restaurant subfile:

$SEARCHINFO returns a null value when the integer does not correspond to a previously-issued search command. For instance, in the example above, $SEARCHINFO(3,com) would be null.

7.7.4a  (The $SEARCHTEST Function)

----------------------------------------
Function: $SEARCHTEST ($SRCHTEST)
Purpose: Test the syntax of the value as
   a search expression; check processing
   rules
----------------------------------------

The $SEARCHTEST function is a one-argument function that takes the form:

where "search-command" is a string containing a SPIRES search command expression, as described below.

SPIRES will check that the statement is valid as a search command for the selected subfile, including passing any search values in the expression through appropriate Searchproc rules. If the expression is valid, the function returns the value "0" (zero) as an integer. A non-zero integer returned by the function indicates an error, which can be further tested with the $MNUM and $ENUM variables. (If $MNUM is 0 when the function returns a non-zero integer, then either searching isn't allowed for the subfile, or the "search-command" string is null; note, however, that these conditions may cause other $MNUM values too.)

The "search-command" in the function should begin with a search command "verb": FIND, AND, OR, AND NOT, NOT (or an allowed abbreviation or symbol). If it does not begin with one of those, SPIRES will add an OR to the front. What follows should be a valid search expression, including search terms (index names), relational operators, and search values. Complex search expressions (search expressions joined with AND, AND NOT and OR, and/or including parentheses) are also allowed, as are expressions with qualifiers and sub-indexes.

Here is an example using a subfile with a KIDS index (Yes/No values) and a JOB index:

This function is particularly handy for checking a search expression without having to incur the expense of SPIRES actually examining index records and creating the search result, which is useful in situations where you want to collect the search commands at one time but have the search done at a later time (perhaps during off-peak hours when rates are cheaper). $SEARCHTEST thus gives you the chance to check that the search commands are valid at the time the user gives them to you, but the search itself is not done till later.

7.7.4b  (The $SET Function)

----------------------------------------
Function: $SET
Purpose: Set the value of a system
   variable: $ASK, $PARM, $PROMPT or
   any Prism system variables
----------------------------------------

The $SET function is a two-argument function that takes the form:

where "system-variable-name" is a string containing the name ASK, PARM, PROMPT or a Prism system variable, such as $MSGLINE, and "expression" is a valid expression that will be converted to a string value and assigned to the system variable named in the first argument.

The function returns the string expression as its value. If the system variable name given is invalid, a null string is returned.

The purpose of this function is to make it easier to assign values to particular system variables. In particular, this lets you set Prism variables from inside Userprocs in record definitions.

Additionally, it also lets you set the value of $ASK or $PROMPT to a variable when that variable's value is an expression that the SET ASK or SET PROMPT command can't handle.

Here is an example showing how easy it is to set the $PROMPT variable with $SET when the value is an expression:

7.7.4c  (The $SETPARMS Function)

-----------------------------------------
Function: $SETPARMS
Purpose: saves its parameter values
   internally until retrieved by
   $GETPARMS function
Related functions: $GETPARMS, $REF
-----------------------------------------

$SETPARMS is a multi-argument function that evaluates and saves its parameter values for later retrieval by $GETPARMS, usually in a subroutine.

The function's form is as follows:

where

7.7.5  (The $SIZE Function)

-------------------------------------
Function: $SIZE
Purpose: Determine a value's string
   length
-------------------------------------

$SIZE is a single argument function of the following form:

The function returns an integer representing the length of the input value, were it converted to string:

7.7.6  (The $SPAN Function)

-----------------------------------------------
Function: $SPAN
Purpose: Return substring of input value broken
   to the left of any character not also found
   in a second string
Related functions: $BREAK, $STRIP
-----------------------------------------------

The $SPAN function complements $BREAK in a way, matching its input string against an inclusion list instead of an exclusion list. (It also complements the $STRIP function, as described further below.)

The function takes the following form:

$SPAN scans the input string looking for characters that are NOT present in string2. The presence of a character in the input string that is not in string2 produces a break in the first string. The function then returns the substring of the input string up to but not including the character that had no match in the string2 list.

If the first character in the input string fails to find a match in string2, then $SPAN returns a null value.

For example:

Note that $STRIP complements $SPAN by returning the part of the input value that $SPAN discards (and discarding the part that $STRIP returns).

7.7.7  (The $SQRT Function)

---------------------------------------------
Function: $SQRT
Purpose: Take square root of numeric value
Related functions: $EXP, $LOG, $MOD
---------------------------------------------

$SQRT is a single argument function of the form:

$SQRT takes the square root of the argument and returns a REAL result. If the argument is negative, $SQRT takes the square root of the positive equivalent.

Here are some examples of $SQRT:

7.7.8  (The $SQU Function)

-------------------------------------------
Function: $SQU
Purpose: Delete blanks from a string value
-------------------------------------------

The $SQU function allows you to delete extra blanks from a string value. Its syntax is:

All leading and trailing blanks will be removed and all multiple blanks in the value will be converted to single blanks. A string value is returned.

For example,

will return

7.7.9  (The $SSW Function)

---------------------------------------
Function: $SSW
Purpose: Determine whether a secure-
   switch is set for the selected file
---------------------------------------

The $SSW function (where SSW stands for "secure-switch") provides a way for you to tell whether your currently selected subfile has a particular secure-switch set. The function is thus somewhat similar to the SHOW SSW command. [For more information on secure-switches, see the manual "SPIRES File Definition", or online EXPLAIN SECURE-SWITCHES STATEMENT.]

$SSW is a single-argument function, whose form is as follows:

    $SSW(ssw-integer)

"Ssw-integer" would be an integer corresponding to one of the secure-switches. (E.g., $SSW(9) corresponds to secure switch 9.) If the subfile has the named secure switch set, the argument is returned by the function; otherwise the function returns a "0" (zero).

For instance, suppose your currently selected subfile has secure switch 3 set, but not secure switch 4:

The value "0" will also be returned if the argument is not the number of a valid secure switch.

7.8.0  (The $STATGET and $STATPUT Functions)

-----------------------------------------------
Functions: $STATGET, $STATPUT
Purpose: Control static variables (e.g., from
          within a format or Userproc)
Related functions: $DYNGET, $DYNPUT, $VARGET,
                   $VARPUT
-----------------------------------------------

The $STATGET and $STATPUT functions help control static variables from inside SPIRES formats or file definition Userprocs where you need access to vgroups allocated outside of the format or Userproc.

The forms of the functions are:

"Variable-name" is the name of the variable to be processed -- its form is analogous to that of the variable named in a LET statement. (Normally, the name is not preceded by a pound sign "#"; if there, SPIRES will replace the name with the variable's value before executing the function, as you'd suspect.)

"Subscript" is the subscript of the variable; use a subscript of "0" or null if the variable is not in an array.

The third argument varies depending on the function. For $STATGET, the third argument is a default value that the function will return if the variable doesn't exist in any allocated vgroup it can access (see below). For $STATPUT, the third argument is the value you want to assign to the variable.

The functions allow a user to retrieve or replace the value of a static variable from inside a format or Userproc. They are most useful when the array the variable is in was allocated globally but outside of the format or Userproc; in those cases, you otherwise cannot access the variable.

In a protocol, the $STATGET function lets you see if a variable with a particular name exists. If the named variable does not exist in an allocated vgroup, then the function returns the default-value.

The function $STATPUT allows you to assign a value to a static variable from inside a format or a Userproc, especially when the variable is in a global vgroup allocated outside of the Userproc or format. The function returns the value of the third argument.

For example:

will assign the value 5 to both #X and #Z.

7.8.1  (The $STRING Function)

------------------------------------------------
Function: $STRING
Purpose: Convert a value's type to STRING
Related function: $CHARACTER
------------------------------------------------

The $STRING ($STR) function is a single-argument function of the form:

$STRING converts its input value to a value of type string.

Note that in many cases the function is superfluous for string conversion, because SPIRES would convert a non-string value to a string automatically. For instance, the slash ('/') prefix automatically converts variables that follow it into variable-length strings. [See 5.17.] In addition, any function that requires a string argument (such as $LEFTSTR in the example below) will always convert a non-string into a string, if such a conversion is possible.

An argument in quotation marks or apostrophes is automatically considered a string value. If you attempt to convert such a value with $STRING, no actual conversion occurs. Likewise, if you attempt to convert the value with $CHARACTER, only the type is changed; the value itself is unaffected. [See 6.2.] For more on the distinction between the $CHARACTER function and $STRING, see $CHARACTER.

7.8.2  (The $STRIP Function)

----------------------------------------------
Function: $STRIP
Purpose: Strip away beginning of string value
Related functions: $BREAK, $RIGHTSTR,
   $RIGHTSUB, $SPAN
----------------------------------------------

The $STRIP function, which creates a substring out of its input string by stripping away the initial characters from it, has the following form:

$STRIP scans its input string from left to right looking for characters that are also present in string2. When a character is encountered in the input string that is NOT also in string2, the function stops scanning, and returns the characters remaining in the input string.

The $SPAN and $STRIP functions are exactly complementary:

7.8.3  (The $SUBSTR Function)

------------------------------------------------
Function: $SUBSTR
Purpose: Make shorter substring of string value
Related functions: $LEFTSTR, $LEFTSUB,
   $RIGHTSTR, $RIGHTSUB, $XSTR
------------------------------------------------

$SUBSTR is a 3-argument function for creating a substring out of the input string given. The function has the form:

$SUBSTR works as follows. First an "integer2" number of characters are skipped to determine the substring's starting point. Then exactly "integer3" characters are returned as the substring. [If integer2 is negative or skips all the characters in the input string, then a null string is returned regardless of the value of integer3. If integer3 is negative or larger than the number of characters remaining in the input string, then the remaining characters are returned.]

Following are some examples of $SUBSTR:

7.8.3a  (The $SYSEVAL Function)

--------------------------------------------------
Function: $SYSEVAL
Purpose: Pass expression to WYLBUR for evaluation
--------------------------------------------------

The $SYSEVAL function lets you pass an expression (for instance a WYLBUR variable or function) to WYLBUR to be evaluated there; the function returns a string value that is WYLBUR's evaluation of the expression. [See 5.1.1 for more on the concept of expressions.]

The form of the function is as follows:

where "expression" represents an expression recognizable to WYLBUR.

The returned value will be truncated to 158 characters.

Here is an example of the $SYSEVAL function:

   -> show eval $syseval(date)
   16:13:56 04/18/86 (86.108)

In this case $SYSEVAL asks WYLBUR to evaluate the WYLBUR variable DATE. A user within WYLBUR who issued the SHOW DATE command would obtain the same value as shown above.

Uses for $SYSEVAL

$SYSEVAL can be a handy way to pass values on to SPIRES that the end-user or application has stored in WYLBUR variables earlier in the session. (These values may well have been established before SPIRES was even called).

There are many other possible uses as well:

    -> if $syseval(lines) ~= 0 then ...   <---(tests for presence
                                              of an active file)

In the example below, a user in SPIRES borrows the WYLBUR function MIN, which has no exact equivalent in SPIRES, to find the minimum value in a set of numerical values stored in a dynamic array:

    -> show dynamic variables
    N::3 = 3
    N::2 = 2
    N::1 = 1
    -> /let leastval = $syseval('min(#n::1, #n::2, #n::3)')
    -> show eval #leastval
    1

$SYSEVAL and the $MSGINT Variable

The system variable $MSGINT records the success or failure of $SYSEVAL. When the function succeeds, $MSGINT is set to 0 (zero); if it fails, an error message is returned and $MSGINT is set to a non-zero value. You can test the variable in your code:

    let author = $syseval(author)
    if $msgint ~= 0 then ...        <---(take action if the
                                        function fails)

7.8.3b  (The $SYSINFO Function)

--------------------------------------------------
Function: $SYSINFO
Purpose: Obtain information on the current system
--------------------------------------------------

The single-argument function $SYSINFO, primarily designed for systems programmers, returns information about the SPIRES or SPIRES-related system that is currently executing. For instance, $SYSINFO returns information about the release date or the link date applying to the currently-executing version of SPIRES.

The form of the function is as follows:

  $SYSINFO(code)

The "code" argument can take the following values:

  Code          Value Returned by $SYSINFO
  ---------     -------------------------------------------
  PROCESSOR     The processor currently running -- e.g.,
                SPIRES, SPIBILD
  LINKDATE      The date (yy.mm.dd) at which this processor
                was last linked as a separate module
  SYSTEM        The version of the system.  Possible values
                returned for $SYSINFO(SYSTEM) include:
                  Prod      Production System
                  PreProd   Pre-Production
                  Test      Test System
                  Dev       Development System
                  Private   Private System
                  Batch     Batch SPIRES
  RELEASE       The system's release date (yy.mm)
  ACCOUNT       The account upon which the system was linked

Values for code can be abbreviated to three characters, e.g.:

  -> show eval $sysinfo(rel)
  87.06

If the "code" you enter is invalid $SYSINFO returns a null string.

7.8.4  (The $SYSTEM Function)

------------------------------------------
Function: $SYSTEM
Purpose: Retrieve fully qualified names
   for files, formats, etc., or validate
   accounts
------------------------------------------

Note: $SYSTEM is designed primarily for SPIRES systems programmers, though the feature for account validation may have a wider use.

The $SYSTEM function retrieves fully qualified names for SPIRES files, global and general file formats, etc., using the abbreviated name provided. It can also be used for account validation as described further below.

The syntax of $SYSTEM is:

where the string often begins with an asterisk, period or dollar sign, and the code is optional. If no code is provided, the function modifies the string as follows: If it starts with a dollar sign, the dollar sign is converted to the system account, followed by a period. If the string starts with an asterisk or a period, that character is changed to the logged-on user's account, followed by a period. If the string consists only of a period or asterisk, then the appended period is omitted. If the string begins with anything else, it is preceded by the logged-on user's account number, followed by a period.

Additional characters are prefixed to the string if one of the following codes is given:

     FULL      -  string is preceded by "ORV."
     GLOBAL    -  string is preceded by "ORV.", unless the string
                  begins with a "$", in which case only the
                  string is returned
     GENFILE   -  string is preceded by "**ORV.", unless the string
                  begins with a "$", in which case only the
                  string is returned

An additional code is available for account validation:

     ACCOUNT   -  the function verifies that the string is in a
                  valid form for an account (ORVYL-STS system) or
                  userid (CMS, MTS, TSO) on the
                  current operating system (see below)

The codes may be abbreviated to three characters.

For example, if the currently logged-on account is GQ.TSI:

The $SYSTEM Function and Account Validation

At STS sites (i.e., Stanford), the ACCOUNT code on the $SYSTEM function verifies that the string given in the first argument is six characters long. (If there is a seventh character it must be the separator character, i.e., a period.) The first and fourth characters must be alphabetic, the second, fifth and sixth characters must be alphanumeric, and the third character must be a period. If the string passes this test, the function returns it in upper case. If the string fails the test, $SYSTEM returns a null string.

7.8.5  (The $TEST Function)

-------------------------------------------
Function: $TEST
Purpose: Test whether a condition is true
   or false
Related function: $EVALUATE
-------------------------------------------

$TEST is a one-argument function, not commonly used, that tests whether a condition is true or false. Its form is as follows:

    $TEST(condition)

where:

The example below indicates how $TEST might be used:

Note that $TEST is not simply testing whether #X is null or non-null (which could be done with a statement beginning IF #X...), but is testing whether the expression contained in the variable is true or false.

In a protocol, expressions can be tested in a similar way with the slash prefix '/':

Thus $TEST will be most valuable for testing expressions in formats and USERPROCs, where the '/' prefix is not allowed.

Note that $TEST can also be used to assign a flag value to a variable:

7.8.6  (The $TIMEIN and $TIMEOUT Functions)

-----------------------------------------
Functions: $TIMEIN and $TIMEOUT
Purpose: Process a time value
-----------------------------------------

The $TIMEIN and $TIMEOUT functions convert time values; specifically $TIMEIN converts a time string to an integer value in millisecond units, and $TIMEOUT converts an integer value in millisecond units (or, less commonly, a hex value in 2-second units) to a time string in one of the standard forms shown in the chart later in this section. Along with many other uses, the two functions can be used together to convert a time value from one format to another (see further below).

The $TIMEIN Function

The syntax for the $TIMEIN function is:

The "time-string" can be any of a wide variety of formats for time, such as "11:20:55" or even "4 Days, 11 Hours, 20 Minutes, 55 Seconds". Note that if only one number is present, SPIRES assumes it is hours; for two, hours and minutes. In other words, $TIMEIN("3") would interpret "3" as "3 hours", or "3:33" as "3 hours, 33 minutes". See the chart below under "Format" for other appropriate formats.

You may also use the words "noon" or "midnight", optionally preceded by the number "12" (or 12:00 or 12:00:00), as the time-string; the case of NOON or MIDNIGHT is not significant.

Aside from the NOON and MIDNIGHT words, be careful not to mix colons and words in the same time-string, and do not use a suffix such as "PM" together with a value for hours that is greater than 12. The maximum time allowed in a time-string is over 24 days; a negative time-string will cause an error and return a null value.

The $TIME variable can be used as input to the $TIMEIN function.

The $TIMEOUT Function

The syntax for the $TIMEOUT function is as follows:

"Time" is most commonly a 4-byte integer in millisecond units. Among other possibilities, it might be the $GETUVAL of an element stored in 4-byte integer form by the $TIME system proc, or it might come from the $TIMEIN function described above. (Less commonly, the input for "time" is a 4-byte or 2-byte hex value, which is interpreted to be in 2-second units.)

"Typecode" corresponds to one of the values shown on the left in the chart below; each typecode specifies a different string format for the output time value. For example if the current value of $TIME is 14:30:25, then using typecode 6 with $TIMEOUT will return the value "14 Hours, 30 Minutes, 25 Seconds". If an error occurs, the string returned by $TIMEOUT will be null.


NOTE: Typecode 15 is the same as typecode 6, but only non-zero units are displayed.

The parenthetical values shown in the chart are only output when the value is non-zero. Decimal points indicate fractional values (output only when the fraction would be non-zero), and triple character designators such as mmm represent values that include all time units greater than this unit. (That is, "2 hours" becomes represented in mmm form as "120".)

Converting the Format of a Time Value

The two functions can be used together to convert a time value from one format to another:

Here are a few brief examples of converted formats, output over a five-second interval at about 2:30 in the afternoon:

Note that the "typecode" you specify may shift a value, so that what you input as hours comes out expressed in minutes. For example, a "typecode" of 2 takes a value input as hours and re-formulates it in terms of minutes.

7.8.6b  (The $TRANINFO Function)

----------------------------------------------
Function: $TRANINFO
Purpose: return information about status of
         the currently open transaction group
----------------------------------------------

The $TRANINFO function is a one-argument function of the form:

where "code" is one of the following:

7.8.7  (The $TRANSLATE Function)

----------------------------------------------
Function: $TRANSLATE
Purpose: Translate a string value
Related functions: $CASE, $CHANGE,
   $CHANGELIST, $UNEDIT
----------------------------------------------

The $TRANSLATE function is a three-argument function of the form:

The value of the input string is altered by replacing any characters in string2 that occur in the input string by the corresponding character in string3.

For example:

String2 and string3 are often the same size. If their size does not match, then the missing character positions are changed to null. Therefore, in the second example, 3 and 4 in argument 2 are changed to null in argument 1 because there are no characters in their positions in string3.

7.8.7.1  (The $TRIM Function)

-------------------------------------------
Function: $TRIM
Purpose: Convert a value to type CHAR.
-------------------------------------------

The $TRIM function is exactly equivalent to $CHARACTER.

7.8.8  (The $TRUNC Function)

---------------------------------------------
Function: $TRUNC
Purpose: Truncate a real value to its
   integer portion
---------------------------------------------

$TRUNC is a one-argument function of the form:

where "real-value" is a real (floating-point) value or a value that is convertible to real. $TRUNC returns the integer portion of the value; that is, the fractional part of any real value is dropped. The returned value is type integer.

Here are some examples of $TRUNC:

7.8a  (The $TYPE Function)

-------------------------------------------
Function: $TYPE
Purpose: Test a value's type
Related functions: $TYPETEST, $RETYPE
-------------------------------------------

$TYPE is a single argument function returning a character string that represents the data type of the value specified in the argument. (Possible data types are string, integer, hex, real, and so on.)

Here is an example of $TYPE:

7.9.1  (The $TYPETEST Function)

------------------------------------------------
Function: $TYPETEST
Purpose: Test conversion of a value's data type
Related functions: $RETYPE, $TYPE
------------------------------------------------

The $TYPETEST function is a two-argument function that checks to see if the first argument, a string or a variable, might be successfully converted to the variable-type specified in argument 2. If argument 1 could be converted to the type specified in argument 2, then the function returns the value of argument 2; if argument 1 could not be converted to the type specified by argument 2, $TYPETEST returns a null value. In other words, you could use the $TYPETEST function to determine if you would encounter a conversion error if you tried to convert a value to a different type.

For example,

SPIRES only reads the first 3 characters of the second argument and interprets the code as follows:

These last three codes are used with triples.

All codes may be abbreviated to one or two characters. The variable $TYPETEST can be used as a TRUE/FALSE condition in an IF statement, because a null return will be interpreted as FALSE while any other returned value will be interpreted as TRUE.

Like the EVAL command [See 5.16.] The $TYPETEST function does not actually perform variable conversion; unlike EVAL, it does not set $NO to TRUE if argument 1 could not be converted to the type specified in argument 2.

Note that a null value (for argument 1) can convert to all types except REF, GRO, NEW and TRI.

7.9.1.1  (The $UNEDIT Function)

-------------------------------------------------
Function: $UNEDIT
Purpose: Strip edit mask characters from a value.
Related function: $CHANGE, $EDIT, $STRIP,
   $TRANSLATE
-------------------------------------------------

The $UNEDIT function allows you to strip a value of all the special characters added by the $EDIT function, such as the dollar sign, commas, "CR". It works somewhat similarly to the $UNEDIT system proc. [For more information on edit masks, see the manual "SPIRES Technical Notes" or online EXPLAIN EDIT MASKS; for more information on the $UNEDIT system proc, see the manual "SPIRES System Procs" or online EXPLAIN $UNEDIT PROC.]

The form of the function is as follows:

where "input-value" is the string to be converted into "unedited" form. The resulting value will be of type string, but it can be converted to a packed decimal value.

For example,

7.9.1.1a  (The $VARGET and $VARPUT Functions)

-----------------------------------------------
Functions: $VARGET, $VARPUT
Purpose: Control variables (e.g., from
          within a format or Userproc)
Related functions: $DYNGET, $DYNPUT, $STATGET,
                   $STATPUT
-----------------------------------------------

The $VARGET and $VARPUT functions can be used to fetch and store variable values. They are particularly useful from inside SPIRES formats or file definition Userprocs in situations where you need access to dynamic variables or to static variables allocated outside of the format or Userproc.

These functions differ from the $DYNGET/$DYNPUT and $STATGET/$STATPUT pairs primarily in the order of their parameters; because their parameter lists end with optional, multiply-occurring "subscript" parameters, $VARGET and $VARPUT allow you to work with multi-dimensional static arrays, which $STATGET and $STATPUT do not easily permit. You may find the order of the parameters on $VARPUT and $VARGET easier to remember.

Also, of course, the $VARPUT and $VARGET functions let you work with any variable, not dynamic variables alone ($DYNGET/$DYNPUT) or static variables ($STATGET/$STATPUT).

The forms of the functions are:

"Variable-name" is the name of the variable to be processed -- its form is analogous to that of the variable named in a LET statement. (Normally, the name is not preceded by a pound sign "#"; if there, SPIRES will replace the name with the variable's value before executing the function.)

The second argument varies depending on the function. For $VARGET, the second argument is a default value that the function will return if the variable doesn't exist as a dynamic variable or in any allocated vgroup that the function can access (see below). The second argument is optional for $VARGET.

For $VARPUT, the second argument is the value you want to assign to the variable. It is required for $VARPUT.

The third and subsequent arguments, "subscript(s)", are optional for both functions. If omitted, then the first occurrence of the variable is assumed. But for multiply-occurring variables, and static variables in multi-dimensional arrays, the subscript arguments specify the desired occurrence.

The $VARGET function returns the value of the requested variable in the same type in which it is stored.

The $VARGET function is also useful for determining whether a variable with a particular name exists. If the named variable does not exist, then the function returns the default-value.

The function $VARPUT allows you to assign a value to a static variable from inside a format or a Userproc, especially when the variable is in a global vgroup allocated outside of the Userproc or format. The function returns the value of the second argument; its type is the same as the type of the second argument.

For example:

will assign the value 5 to #Z as an integer (assuming #Z is not defined in an allocated vgroup as a different type), and will return the value 5 as an integer. Continuing the same example:

will return the value 5 as an integer.

If the variable you are retrieving or storing is in a multi-dimensional static array, you can name the absolute occurrence number of that variable within the array as the first and only subscript number. For example, if ABC is a two-dimensional array with two occurrence per dimension, then the command SHOW STATIC VARIABLES will display the array like this:

You could retrieve the final value in the array with either of these functions:

(As usual, remember to count occurrences of variables from zero.)

7.9.1.2  (The $VARTEST Function)

-------------------------------------------------
Function: $VARTEST
Purpose: Determine attributes of static variables
Related function: $TYPETEST, $TYPE
-------------------------------------------------

The $VARTEST function provides several types of information about attributes of allocated static variables, such as their type, their length, maximum number of occurrences, etc.

The $VARTEST function's syntax is:

where "variable-name" is the name of a static variable in an allocated vgroup. (Don't put the "#" ahead of the name unless you want substitution to take place before the function is evaluated; that is, unless the variable in the function contains the name of the variable you really want the information for.) The "code" is one of the following:

The second form of the function, with the "index" parameter, is for use with variables with the INDEXED-BY statement coded in their definitions. The "index" is an integer from 1 up that represents a particular array dimension for the named variable. So, for example, if variable ABC is defined as "INDEXED-BY = DIMENSION1, DIMENSION2;", then you would specify "1" as the index value to get information about #DIMENSION1 as an indexing variable to ABC.

Only two of the codes listed above work with that form: OCCURRENCES, which shows the number of occurrences of the variable for the specified "index" dimension; and INDEXED, which names the variable that serves as the index for the specified "index" dimension.

Here are some examples of $VARTEST and the values it would return. Suppose you have an allocated vgroup that contains the following variable (shown here in its variable definition):

Here is $VARTEST in use:

7.9.1.2b  (The $VERIFY Function)

-------------------------------------------------
Function: $VERIFY
Purpose: Verify characters in input string are
           all like or unlike characters in a
           second string
-------------------------------------------------

The $VERIFY function compares all the characters in the input string with the characters in a second string; depending on what you request, it verifies either that all the characters in the input string can be found in the second string (LIKE), or that none of them can be found there (UNLIKE).

The $VERIFY function's syntax is:

where "input-string" is the string whose characters you want to check, and "characters" is the list of characters to which to compare the input string.

The function returns "1" ($YES) if the function is satisfied; it returns "0" ($NO) if not. The function also returns "1" if the input string is null.

Here are a couple examples of its use:

That function is checking that $ASK contains only capital letters. Values for $ASK like "howdy" and "HELLO THERE" would fail (the latter because of the blank, which is not in the characters string); Values for $ASK like "GREETINGS" and "" (null) would pass.

That function is checking to make sure that $VAL doesn't contain any of the special characters listed in the characters string, which you might notice includes a blank and a comma.

7.9.1.3  (The $VGROUPALTER Function)

-------------------------------------------------
Function: $VGROUPALTER ($VALTER)
Purpose: change the size of an allocated vgroup
          by changing the size of an array
Related functions: $VGROUPINIT ($VINIT)
-------------------------------------------------

The $VGROUPALTER function lets you manage vgroup array space dynamically. It will reallocate the vgroup in memory, shrinking or enlarging the vgroup's size as necessary, changing the size of an array within the vgroup.

Here is the function's syntax:

where "vgroup-name" is the name of the static vgroup whose size you want to change. "Variable-name" is either the name of a one-dimensional array whose size you want to change, or is the name of an index variable for one or more multiple-dimension arrays in the vgroup. "Occ-val" is an integer (its value must be greater than 1) representing the number of occurrences you want for the one-dimensional array or the number of occurrences within the dimension of the index variable for the multiple-dimension arrays it indexes. (Note that it will take multiple uses of $VGROUPALTER to change multiple dimensions of an array.)

$VGROUPALTER is most often coded in EVAL commands or Uprocs; no value is returned by the function. If it fails, an S-level error is reported.

Suppose, for instance, that you will be reading data from a record into an array but the size of the array will vary widely, depending on the size of the record. You might initially allocate the array in the vgroup with the normal number of occurrences you expect for the array, and then, when you know a particular record will be too large, reallocate the vgroup with $VGROUPALTER to exactly the size you need. That way, you don't have to allocate perhaps hundreds of extra occurrences of the array that you will not need to handle the largest possible (but seldom occurring) record, which could waste many bytes of internal memory.

For a specific example of how to code $VALTER, suppose in vgroup LOCAL, you want to raise the size of two arrays that use the integer variable SUBCOUNT as an indexing variable. In their definitions, the two arrays each have 5 occurrences listed for the dimension indexed by SUBCOUNT, but you would like to bump that dimension to 10 occurrences. Here is the $VALTER function you would use:

Important notes about $VALTER:

7.9.2  (The $VGROUPINIT Function)

-------------------------------------------
Function: $VGROUPINIT  ($VINIT)
Purpose: Reinitialize the variables of
   the named vgroup
Related function: $ASET, $VGROUPALTER
-------------------------------------------

The $VGROUPINIT function, alias $VINIT, re-establishes the values that the variables in a particular vgroup had when it was first allocated. The function's form is:

  $VGROUPINIT(vgroupname)
      or
  $VINIT(vgroupname)

where "vgroupname" is the name of the vgroup whose values are to be initialized. This name should be in one of the forms allowed for the SET VGROUP or ALLOCATE command, such as "ORV.gg.uuu.testvars". [See 4.2.1 for other forms of "vgroupname".] It is quite important to include the "ORV" prefix on the "vgroupname", if your vgroup will be allocated or used by another account besides your own. For example:

  -> eval $vinit(orv.gq.pro.testvars)

$VGROUPINIT "zeroes out" the variables in the vgroup that it names. However, if any of the variables were given initial values in their vgroup definition (through the VALUE statement), $VGROUPINIT will reset the variables to those initial values. [See 4.1.1.]

Only the variables in the named vgroup are affected; no dynamic variables will be affected, including any declared in the vgroup definition. To clear dynamic variables, use the $ZAP or $DYNZAP functions, or the command CLEAR DYNAMIC VARIABLES.

7.9.3  (The $WDS Function)

--------------------------------------------
Function: $WDS
Purpose: Convert a value's type to WDS
Related function: $CHAR, $STRING
--------------------------------------------

$WDS is a one-argument function, rarely used today, that converts its argument to type WDS.

7.9.3.1  (The $WINDOW Function)

For an explanation of $WINDOW, see $PRECISION function.

7.9.3a  (The $WORKDAYS Function)

---------------------------------------------
Function: $WORKDAYS
Purpose: Find a date x number of working days
   away
Related functions: $DATEIN, $DATEOUT, $DAYS
---------------------------------------------

The $WORKDAYS function returns the date of the nth workday either before or after a specified date. It bases its calculations on days of the week (Monday-Friday) plus information on Stanford University holidays as found in the System Calendar.

The syntax of the $WORKDAYS function is:

where:

The number of working days may be a positive or negative number and it will return the date as a string in the form mm/dd/yy. If "0" is used, then the function will return a date value if "date" is a workday but null if it is not.

Examples of the $WORKDAYS function:

 $WORKDAYS(1)            <-- returns next working day from today
 $WORKDAYS(3)            <-- returns third working day from today
 $WORKDAYS(-1)           <-- returns previous working day from today
 $WORKDAYS(5,'12/22/92') <-- returns '12/31/92' (skipping Christmas holidays)

 $WORKDAYS(0,'1/15/93')  <-- returns '01/15/93' because it's a workday
 $WORKDAYS(0,'1/17/93')  <-- returns '' because it's a holiday
                               (Martin Luther King day)

7.9.3b  (The $XDATE Function)

---------------------------------------------
Function: $XDATE
Purpose: Reformat date value from days into
   standard date form
Related functions: $DATEIN, $DATEOUT, $DAYS
---------------------------------------------

$XDATE is a one-argument function that takes an integer value representing the number of days since Jan. 1, 0000, and converts it into the 4-byte hexadecimal form of a date (in the form CCYYMMDD). It is used to reconvert values established by $DAYS and arithmetic into the standard SPIRES form of a date.

In the example below, the $DAYS and $XDATE functions take today's date (held in unconverted form in the $UDATE variable), and use it to establish the date five days into the future:

7.9.4  (The $XEQSTACK Function)

-------------------------------------------
Function: $XEQSTACK
Purpose: Tracks nesting of execution levels
   in protocols
-------------------------------------------

The $XEQSTACK function returns the XEQ command that nested execution to the level specified in its argument. The function is especially useful for debugging applications with complex nesting of commands.

$XEQSTACK has the following form:

where:

(A null response to $XEQSTACK indicates that there was no command at the specified level.)

For instance, consider a protocol that nests in the following way:

 * LEVEL0
 XEQ PROC LEVEL1
 RETURN
      :
 ++LEVEL1
 XEQ PROC LEVEL2
 RETURN
      :
 ++LEVEL2     <-- At LEVEL2, $XEQSTACK(0) returns XEQ PROC LEVEL2
      :                      $XEQSTACK(1)    "    XEQ PROC LEVEL1
 RETURN                      $XEQSTACK(2)    "    XEQ FROM LEVEL0
                             (or XEQ USING if protocol executes
                             from the active file)

Compare the SHOW XEQ STACK command, which displays the entire stack of XEQ commands at once. [See 3.2.2.] See also the $XEQLVL variable, which tells the level at which an executing protocol is nested. [See 6.4.]

The maximum number of nesting levels is 128.

7.9.5  (The $XSTR Function)

----------------------------------------------
Function: $XSTR
Purpose: Convert a string (or hex) value
Some related functions:  $LEFTSTR, $LEFTSUB,
   $RIGHTSTR, $RIGHTSUB, $SUBSTR
----------------------------------------------

The $XSTR function returns a substring of its input string, deleting the front or the back of the input value, depending on parameters that you specify. In addition, the function can be used to convert a hex value instead of a string value.

The syntax for the $XSTR function is as follows:

where

Here are some examples of $XSTR at work. Assume #X = 'MISTRUST'.

The function would work similarly with hex values.

Note that the combinations "Keep,First" and "Drop,First" make the function equivalent to $LEFTSTR and $RIGHTSTR respectively.

7.9.5a  (The $XSUB Function)

----------------------------------------------
Function: $XSUB
Purpose: Get substring of value based on
          substring within the value
Some related functions:  $XSTR, $LEFTSUB,
   $RIGHTSUB
----------------------------------------------

Like $XSTR, the $XSUB function returns a substring of its input string, deleting the front or the back of the input value, depending on parameters that you specify. Unlike $XSTR, which uses physical character positions to determine what to return, $XSUB returns a substring of the original string that precedes or follows another substring specified in the function. So, for instance, $XSUB might return the value to the left of the first period in the input value, wherever it appears within the value.

In addition, the function can be used on a hex value instead of a string value.

The syntax for the $XSUB function is as follows:

where

Here are some examples of $XSUB at work. Assume #X = 'MISTRUST'.

The last example shows that $XSUB returns a null value if the "substr" cannot be found in the input string.

The function would work similarly with hex values:

The combinations "Left,First" and "Right,First" make the function equivalent to $LEFTSUB and $RIGHTSUB respectively.

7.9.5b  (The $YYCALC Function)

----------------------------------------------
Function: $YYCALC
Purpose: Add or subtract years to a 2-digit
          year, or convert to a different format
Some related functions:  $YYTEST
----------------------------------------------

Arithmetic with years stored as 2-digit values (97 for 1997, for example) can run into problems at century boundaries, as the Year 2000 problems across the computer industry prove. The $YYCALC function supports basic addition and subtraction for two-digit years (in a variety of forms), providing wrap-around when the result crosses a century. Additionally, you can request the output in a different form than the input date, providing a means of converting a 2-digit year to another format; for example, you can request that the 2-digit year (99, perhaps) be output as a 4-digit one (1999). Some of the allowed forms for input and output allow an additional digit representing the quarter of the year (QYY or YYQ forms, for instance).

The syntax of the $YYCALC function is:

where:

  (*) I    - an integer from 0 to 199 representing the year
              1900 plus the integer (e.g., 103 means 2003).
              Integers less than the "wrap" parameter value are
              bumped by 100 to place them in the window range.
      YY   - a two-digit year, 00 to 99.  If you input a single digit,
              a "0" is prefixed to make it a YY value.
      HY   - a two-digit year, 00 to 99, continuing with sequentially
              higher values for the first "digit": first, hex FA
              for the first digit (hex FA concatenated to 0, hex FA
              concatenated to 1, etc.) representing 2000, 2001, etc.
              then hex FB concatenated to 0 (2010), up to hex FF
              concatenated to 9 (2059).  If you input a single digit,
              a "0" is prefixed to make it a HY value.  (See examples
              below about the hex forms for input and output.)
      YYQ  - a two-digit year, plus a quarter (0 through 4)
      HYQ  - the same as HY but followed by the quarter (0-4)
  (*) QYY  - the quarter (0-4), followed by a two-digit year
  (*) QHY  - the same as HY but preceded by the quarter (0-4)
      CCYY - a four-digit year, e.g., 1997
  (*) TERM - a four-digit value of the form: CYRN (explained later).

The TERM form is a PeopleSoft variation of CCYY and Q called CYRN. On input, all values are first converted to CCYY and Q. Forms CCYY, I, YY and HY supply Q=0. If a TERM of the form CYRN is input, Q=N/2 and CCYY=CYR+2000-101. CCYY and Q are then converted to the requested output form. If a TERM is to be output, N=2Q, and CYR=CCYY+101-2000.

For example, $YYCALC(394,,QYY.TERM) creates 0956. That's because YY of 94 becomes CCYY of 1994 which becomes CYR of 095 when you add 101 and subtract 2000. Meanwhile, Q of 3 becomes N of 6.

The following table gives a range of examples:

     YYQ    TERM            YYQ   TERM            YY   TERM

     981    0992            001   1012            95   0960
     982    0994            002   1014            13   1140
     983    0996            003   1016
     984    0998            004   1018          CCYY   TERM
     991    1002            011   1022
     992    1004            012   1024          1985   0860
     993    1006            013   1026          2004   1050
     994    1008            014   1028          2015   1160

Here are some sample calculations and conversions using $YYCALC. Note in all cases that the default "wrap" value of 30 is used, and that when no convert-form parameter is specified, the default output form is YY.

First are some basic examples:

Range examples

The "wrap" value defines a specific 100-year range for input interpretation and allowed output for all forms except I and CCYY.

  |------------wrap=30-----------------|
            |--------------wrap=50--------------|
1930      1950        1999  2000     2029     2049 ...

For instance, using the default "wrap" of 30, the allowed range is 1930-2029. If you add 1 to the input value of "29" (which, using the default "wrap" of 30, represents 2029) the result is 2030; if you request the YY form of output, 2030 is outside the 100-year range, and so a null value is returned, representing an error:

Examples of the "input-form.output-form" format for "convert-form"

Here you want to convert the input value from its QYY form to YYQ:

Here you want to convert the input value to the number of years it represents since 1900:

In that particular case, the input-form is optional, since a 4-digit input value is assumed to be in CCYY form. Similarly, a 3-digit input value is assumed to be in YYQ or HYQ form, and a 2-digit input value is assumed to be in YY or HY form. For clarity's sake, you may want to declare the input form, as shown in the last example; however, be aware that if the input value does not match the form (for instance, for ccyy, if the input value had been 13 instead of 2013), then an error occurs, which would not happen if the input-form were omitted.

For type I, integer input values above 199 are treated as though they were the special value: 255. Likewise, the 3-digit input-forms allow 999 as a special value, and the 4-digit input-form allows 9999 as a special value. Attempts to increment (or decrement) these special values are ignored, and the output will be the special value associated with the output-form. For example, $YYCALC($INT(200),5,I.QYY) will create 999 as an answer. Likewise, $YYCALC(999,,YYQ.I) will create the integer value 255.

Examples of the hex forms

The three hex forms, HY, HYQ and QHY, appear as part of a special method of handling Year 2000 problems. In essence, they provide a trick answer to the question, "What two-digit number is greater than 99?" In a string value, the digits 0 through 9 are stored internally as F0 through F9; 99 is thus F9F9 in hex. To provide a two-character "number" that is higher than 99, SPIRES can follow hex F9 with hex FA, hex FB and so on, to hex FF; when these hex values are used as the first character in a two-character number, they extend the two-character number range up to 159. This lets applications store a two-digit representation of the year 2000 and higher that will sort after "99", unlike the simple wrap-around to "00" for 2000. (Of course, other steps must be taken for displaying these special hex dates correctly, or working with them in other ways.)

To describe the hex forms a different way, using the hex form HY as an example, the numbers include the YY range from F0F0 to F9F9, but then continue with FAF0 (representing 2000) through FFF9 (2059). For input, HY values are interpreted according to the "wrap" range as shown in this example with wrap=60:

                      |-------------wrap=60--------------|
F0F1    F3F0    F5F9 F6F0       F9F9  FAF0     FCF9     FFF9
2001    2030    2059 1960       1999  2000     2029     2059

If you use a hex form, keep in mind that the $YYCALC function still has only a 100-year range for output, defined by the "wrap" parameter, as in:

            |------------wrap=30-----------------|
                      |--------------wrap=60--------------|
F0F1      F3F0      F6F0        F9F9  FAF0     FCF9     FFF9
1901      1930      1960        1999  2000     2029     2059

So, for example:

But hex FAF5 would be invalid input for the yy.hy option because it is not a yy input ranging from 00 through 99.

The following chart shows corresponding output values in the range for wrap=30 using CCYY, HY, YY, and I output-forms. The notation <FAF0> is an alternative way of showing hex FAF0, which is equivalent to $RETYPE($HEX(FAF0),STRING).

 CCYY     HY     YY     I
 1930     30     30    30
 1999     99     99    99
 2000  <FAF0>    00   100
 2029  <FCF9>    29   129

Examples of quarter forms

The YY and HY forms also allow you to specify a quarter value, from 1 to 4 (with 0 or a blank, i.e., a two-digit value, allowed to indicate no quarter), useful in some applications that work with a quarter system. So, for instance, if the form is QYY, then 499 means "fourth quarter 1999".

Interpretation and meaning of the quarter indicator is relative to the application. For instance, a QYY of 499 may mean the last three months of the calendar year 1999 or the last three months of the school year 1999-2000 (i.e., June through August 2000). That is especially important to remember when you are using $YYCALC to do conversions:

Both the input and the output must be interpreted the same: if 497 means the 4th quarter of the school year 1997, then the 1997 result represents the school year 1997. The mistake to avoid is thinking that you are converting from the school year to the actual calendar year; in other words, it is a mistake to think that the example above shows that the 4th quarter of the school year 97 is in the calendar year 1997.

In performing calculations with quarter forms, SPIRES inserts a placeholder "0" for the quarter when an input value lacks one. This means that 970 or 97 will sort before 971, 972, etc. (This is more significant for the $YYTEST function, described in the next section.) [See 7.9.5c.]

7.9.5c  (The $YYTEST Function)

----------------------------------------------
Function: $YYTEST
Purpose: Compare 1 through 4 digit year values
          using a century wrap-around value
Some related functions:  $YYCALC
----------------------------------------------

When years are stored as 2-digit values (97 for 1997, for example), it can be problematic to compare them, since in some contexts an absolutely smaller number ("00", for instance) may represent an earlier year (1900) but in other contexts it may represent a later one (2000). The $YYTEST function provides a way to compare two years, each of two digits. It also allows an additional third digit on the dates, representing a quarter value, that is, the form HYQ, as in 971 for "first quarter 1997".

The syntax of the $YYTEST function is:

where:

All comparisons are done with CCYYQ values. HY and CCYY assume Q=0. HY and HYQ have the HY converted to CCYY form before the comparison, as shown in this example with wrap=60:

                      |-------------wrap=60--------------|
F0F1    F3F0    F5F9 F6F0       F9F9  FAF0     FCF9     FFF9
2001    2030    2059 1960       1999  2000     2029     2059

If an error occurs doing conversions, the original input strings are compared.

The function returns $TRUE (1) if the comparison is true; it returns $FALSE (0) if it is not true.

Here are some sample comparisons using $YYTEST. Note in all but the last case that the default "wrap" value of 30 is used.

In this final example, the first value being compared starts in the QYY form, but is converted to the HYQ form by the $YYCALC function so that it can be used in $YYTEST for comparison with a user variable, #compdate, which has an HYQ value representing the first quarter of 2005:

In other words, the third quarter of 2004 is not greater than the first quarter of 2005.

7.9.6  (The $ZAP Function)

---------------------------------------------
Function: $ZAP
Purpose: Eliminate a dynamic variable or
          dynamic element
Related function: $DYNZAP
---------------------------------------------

The $ZAP function eliminates the dynamic variable named with it in a LET statement. Its form is as follows:

where "dynvar" is the dynamic variable to be eliminated. $ZAP eliminates the specified dynamic variable (and its value), thereby freeing the space that the variable was taking in computer memory. [See 8.2.]

Note that the $ZAP function has no arguments and must be the only term on the right side of the equal sign in a LET statement. Within a format or USERPROC, the $DYNZAP function must be used instead of $ZAP to eliminate a dynamic variable.

The $ZAP function can also be used to "undefine" a dynamic element when used in the DEFINE ELEMENT command:

where "elemname" is the name of the previously defined dynamic element. This can be done more straightforwardly with the CLEAR DYNAMIC ELEMENT command, however.

7a  System Variables and Functions Associated With Triples

In SPIRES protocols and in most programming languages, "variables" are the association of two entities: the name of a field and its value. "LET DAY = MONDAY", for example, is the association of a field named DAY with a value MONDAY. A "triple" is fundamentally different: it allows the association of more than two entities; it is usually used to associate three entities -- hence the name "triple" or "3-tuple" -- though it can be used to associate two or more entities. A collection of triples can themselves be associated into an entity called a "group".

The three entities associated by a triple are called the "attribute", the "object" and the "value" for purposes of formal description and nomenclature. For example, four different triples might be made from the following data:

              1.            2.           3.           4.

Attribute:  Birth Date   Sex          Birth Date    Employee Number
Object:     Ken Chandler Ken Chandler Maggie Horton Maggie Horton
Value:      07/27/45     Male         04/07/43      172426

Triples 1 and 2 might form one group, based on a common "object"; 3 and 4 another group, also based on a common "object"; and 1 and 3 another group, based on a common "attribute".

Within a triple, the attribute, object and value can be variables or values of any type, including triples themselves; when the component parts of a triple are themselves made up of one or more triples, then more than three entities can be associated. In this sense, triples are like multi-dimensional arrays where the indexes of the dimensions need not be integers.

Thus, triples are an association of three values, given that a value itself may be a triple. Groups are an association of "n" values, each of which must be a triple; groups are "n-tuples" which may be referenced like a list structure.

Once made (i.e., an association of entities having been constructed), triples can then be looked up (i.e., retrieved or evaluated) by specific criteria (attribute, object, and value specified) or generic criteria (attribute or object or value specified), or the triples can be combined in groups by criteria. The system functions and variables described in this chapter accomplish these manipulations.

Note that triples and groups are like dynamic variables in that they cannot be stored in a static VGROUP.

7a.1  (The $MAKE Function)

The $MAKE function is used to create or "make" a triple from three components; it is a three-argument function that returns the address of the triple.

$MAKE provides two ways of making a triple:

For example:

EVAL makes the triple but doesn't store the address. The address of a triple is like the social security number of a person: in itself it contains no useful data about the person, but it is useful as a unique identifier to use to look up other information. It is most common to use EVAL to make a triple; the address can be obtained later by the $LOOKUP function, [See 7A.7.] if necessary.

The address of a triple cannot be displayed by a * command. Use the SHOW TRIPLES command to display the address and the values associated with triples. [See 7A.13.] Alternatively, you can use the $RETYPE function (with HEX) to see a hexadecimal translation of the address: $RETYPE(address,HEX).

The address of a triple must be stored into a dynamic variable, if it is stored at all. In the above example, if the TRIADD variable were to be in a VGROUP, the variable must be declared as TYPE=DYNAMIC. Storing the address of a triple into a dynamic variable causes the variable to take on TYPE=TRI:

$MAKE unconditionally creates the triple, whether or not it existed before; use the $MADE function to determine if the triple already exists. [See 7A.4.]

Note that the exact form of the components is important; "Birth Date", "BIRTHDATE" and "BIRTH DATE" are three different attributes. Also note that components with special characters (including blanks) must be enclosed in apostrophes or quotes as with other SPIRES functions.

7a.2  (The $NEW Variable)

$NEW is a variable that returns a number 16 greater than the last time it was used. This variable is useful in making a unique triple when fewer than three components are available.

For example, the following three commands make three unique triples:

$NEW is generally used in functions associated with triples, as shown above; but the value of $NEW may be assigned to a dynamic variable for subsequent use:

7a.3  (The $ANY Variable)

The $ANY variable is used in triples functions in much the same way the "?" is used in the $MATCH function: $ANY specifies that anything in that position qualifies. $ANY can be substituted for the attribute, object, and/or value specifications in functions associated with triples, to allow specification of "generic" criteria when used in place of one of the components. Note that $ANY, when used, must entirely replace a component.

When $ANY is used to specify generic criteria in triples functions, it causes a scan of all triples to locate those that satisfy the criteria. This is less efficient than the use of specific criteria for the attribute, object, and value, in which case no scanning is done by the system to locate the triple.

$ANY is a variable of TYPE=NEW.

7a.4  (The $MADE Function)

The $MADE function returns the integer number of triples that satisfy the generic or specific criteria that are the arguments of the function; $ANY [See 7A.3.] is used to specify generic criteria.

$MADE is a three-argument function of the form:

where attribute, object, and/or value can be "$ANY".

For example:

7a.5  (The $UNMAKE Function)

$UNMAKE is a three-argument function that unmakes the triple(s) specified on the basis of generic or specific criteria. It returns the integer number of triples unmade.

The forms for $UNMAKE are:

where attribute, object, and/or value can be replaced by $ANY. The command

unmakes all triples.

Note that $UNMAKE is somewhat similar to $MAKE in that it does not report an error if its action has already been done. That is, $UNMAKE simply guarantees that the triple(s) does not exist; if you try to UNMAKE a triple that does not exist, no error is reported, but a zero integer value is returned.

7a.6  (The $UNMAKETRIPLE ($UNMAKETRI) Function)

$UNMAKETRIPLE is a function with a single argument that is the address of the triple to be unmade. It allows you to unmake a triple at a specified address without knowing or specifying any of its component parts.

The forms of the function are:

where the address is usually a variable that has been assigned a triple address, and "integer" is the "use-count" of the triple.

For example:

Note that the "use-count" of a triple, returned by $UNMAKETRIPLE, indicates the number of variables that were directly assigned the address of the triple (T1, T2, and T3 in the above example). When these references to the triple are erased (by $ZAP), then the use-count is decremented. Until the use-count of a triple is 0, the triple continues to exist -- that is, it may be referenced by functions that have the address of the triple as their argument -- but it does not exist in its own right and is unavailable to triples functions that require specification of components, such as $MADE.

7a.7  (The $LOOKUP Function)

$LOOKUP is a three-agrument function that returns the address of the first triple that satisfies the generic or specific criteria supplied as the arguments of the function.

The form of the function is:

where attribute, object, and/or value can be specified as $ANY to indicate generic criteria.

If $LOOKUP can not find any triple, $TRINULL is returned. Therefore, if #address = $trinull then * no triple was found.

Note that criteria must be specified in the exact form used when the triple was made; e.g., criteria are case-sensitive.

7a.8  (The $ATTRIBUTE ($ATTR), $OBJECT ($OBJ), and $VALUE Functions)

$ATTRIBUTE, $OBJECT, and $VALUE are functions with a single argument that specifies the address of a triple and returns the requested component of the triple at that address. These functions are used to isolate one component of a triple given the triple's address.

The form of each of the functions is:

where the address is obtained from functions such as $MAKE and $LOOKUP for example.

For example:

Note that the type of the data returned from these functions varies depending on the type of the component when the triple was made. For example:

If the attribute, object, and/or value used to make the triple was itself a triple, then $ATTRIBUTE, $OBJECT, and/or $VALUE return the address of that triple; the $ATTRIBUTE, $OBJECT, and/or $VALUE functions can then be reissued using the new address to retrieve the specified component. For example:

If a triple was made using variables for one or more of its components, then the triple is an association of the values of those variables when the triple was made; the value of the component will not change as the value of the variable used for that component changes. For example:

But note that the value of a component can be the name of a variable, rather than the value of the variable. For example:

7a.9  (The $GROUP ($GRP) Function)

"Groups" are an association of any number of entities, all of which must be triples. $GROUP is a three-argument function that makes a group out of all triples that qualify on the basis of generic criteria, and returns the "address" of the group.

The form of the function is:

where attribute, object, and/or value can be replaced by $ANY. The address of the group is important, since it is necessary in all subsequent functions that operate on the group.

If $GROUP can not find any triples to form a group, then $GRPNULL is returned. Therefore, if #address = $grpnull then * no group was formed.

A group is destroyed with the $ZAP function used on the variable containing the group's address; only the grouping itself is eliminated, not the individual triples that made up the group.

For example:

Note that the "address" returned from the $GROUP function can be tested like a flag variable to determine if a group was created.

For example:

Note that, like a triple's address, the address of a group cannot be displayed with a * command. The address of a group, when stored, must be stored into a variable of TYPE=DYNAMIC; the variable becomes TYPE=GRP:

Addresses of triples and groups cannot be compared to non-triple and non-group addresses in IF statements:

7a.10  (The $GROUPSIZE ($GRPSIZE, $GRPSZ) Function)

$GROUPSIZE is a function with a single argument of the address of a group; it returns the number of the triples in the group.

The form of the function is:

where the address of the group is obtained from the $GROUP function.

For example:

7a.11  (The $GROUPSORT ($GRPSORT) Function)

$GROUPSORT is a two-argument function that specifies the address of the group and the component to be sorted on, and returns the address of a new (sorted) group.

The form of the function is:

where the words "ATTRIBUTE", "OBJECT" or "VALUE" can be abbreviated to one or more characters.

The "address" value is frequently needed when a group is created and sorted in a single command; the address is TYPE=GRP. For example:

If the address returned is "0", then no group was created. The sort of a group is always done in ascending character (unsigned) sequence. This gives the following sequence, for example: $INT(0), $INT(1), $INT(2), ... '.', 'a', 'b', 'c', ... 'A', 'B', 'C', '0', '1', '2', $NEG(2), $NEG(1). Note that the sorting of a group does not maintain the input order of the triples in the group; that is, first in, first out is not guaranteed.

7a.12  (The $GROUPELEMENT ($GROUPELEM, $GRPELEM) Function)

$GROUPELEMENT is a function whose two arguments specify the address of a group and the ordinal number of a member (or "element") in the group. It returns the address of the triple at that position in the group.

The form of the function is:

where the "element-ordinal" is an integer string ranging from 1 (indicating the first member of the group) to the size of the group.

For example:

7a.13  (The SHOW TRIPLES (SHO TRI) and CLEAR TRIPLES (CLE TRI) Commands)

The CLEAR TRIPLES command unmakes all triples; it is equivalent to $UNMAKE($ANY,$ANY,$ANY). [See 7A.5.] A subsequent $LOOKUP($ANY,$ANY,$ANY) [See 7A.7.] will find no triples that exist in their own right (though references to triples' addresses may still exist via variable references).

The SHOW TRIPLES command lists all triples made in the session, including triples that have been unmade but are still referenced (i.e., the "use-count" [See 7A.6.] of the triple is non-zero). The display from the command shows the address of the triple, followed by its attribute, object, and value components (any or all of which may themselves in turn be an address).

For example:

The "X" address prefix indicates that the triple has been unmade -- i.e., no longer exists in its own right and can no longer be located by $LOOKUP -- but continues to exist because it is still referenced. If all the variables assigned the triple's address are deleted via the $ZAP function, then the triple ceases to exist at all, and SHOW TRIPLES will not display it.

The "%" address prefix indicates that the triple exists in its own right -- i.e., has not been unmade -- and may be referenced. If the "%" prefix appears on one of the components of a triple (as in the second line of the display above), then that component itself is a triple.

The SHOW DYNAMIC VARIABLES command [See 8.2.] can be used to display variables whose value is the address of a triple. Usually, a dynamic variable whose value begins with a "%" is a variable containing the address of a triple. The existence of groups is shown by the SHOW DYNAMIC VARIABLES command; groups themselves are not displayed by SHOW TRIPLES.

7a.14  (Using Triples -- Some Guidelines and Examples)

To a greater extent than other functions in SPIRES, the functions associated with triples only accomplish useful work when they are used in combination. Thus, it is important to understand how combinations of triples functions are usually used.

The types of applications in which triples are useful are usually those in which associations must be made between items whose domain (i.e., range of values) is not predictable or well-bounded. Triples are also useful in applictions where associations must be formed among more than two things, and where the typical approach of using large and/or multiple arrays would be cumbersome because of core or other limitations.

The typical use of making associations while processing data then reporting on the associations made at the end of processing can generally use the following recipe:

A second useful example of a programming technique using triples involves the association of several components into a single unit. Typically, this kind of processing is done with several arrays, having all of their occurrence-counters matching each other. But this approach may require large arrays, with careful programming to keep them synchronized, and several statements to set and examine values in each of the arrays. Using triples, the existence of a unit can be created with a single command; the existence of a specific unit, or of a class of units, can then be detected at a later point in the program with a single command.

This type of processing has been used to handle error message processing in full-screen applications where complex screens have multiple levels of error messages, and the screen design itself is subject to change. When an error is detected, the controlling format can make a triple from components that are the row and column numbers, the error level, and the message number; if no error is detected, then the triple would be unconditionally unmade.

A label group in this format might look as follows:

A frame dedicated to error-message display can have a single label-group executed repeatedly to process all error messages:

Subsequent processing can force the user to correct all serious-level errors, and can avoid renotifying the user of warning-level errors by eliminating the triples that indicate they exist:

Reprompt processing will continue until no serious errors are detected:

8  Protocol Debugging

There are several capabilities available to facilitate protocol debugging. These are diagnostic messages, display of variable values, and command tracing. Each is discussed briefly below.

8.1  Diagnostics

The list below shows some (by no means all) of the diagnostic messages issued by the system when protocol errors are encountered. In general, when an error occurs, the execution is broken back to COMMAND mode, and the "-Continue XEQ?" prompt is displayed. For instance:

Some common diagnostics are:

 -'++label.name' not found  [if a jump specifies a non-existent label]
 -Variable value for variablename incorrect type
 -Cannot 'XEQ FROM' this subfile
 -Allowed only in XEQ
 -Nothing to continue
 -Nothing to break
 -Bad length or value
 -Nothing to XEQ
 -Not enough core for XEQ
 -No such variable
 -Unrecognized: [text of statement whose syntax is faulty]
 -Not a legal or complete command
 -Conversion error: type to type,  value = constant

8.2  Display and Modification of Variable Values

If you answer 'no' to the 'Continue XEQ?' prompt, you are returned one level, usually back to command mode, signified by the '->' prompt. However, if you answer 'break' to the 'Continue XEQ?' prompt, you cause a temporary execution break in the protocol -- you can later continue executing it from the point where you left off. [See 2.4.]

In either case, you can display any variable known to the system by using the command forms:

BREAK XEQ State

Answering 'break' places you in a BREAK XEQ state, from which the command

can be issued, displaying the protocol statement that the system was trying to execute when an execution break was caused; it is usually the statement that caused the 'Continue XEQ?' prompt.

In addition, while in BREAK XEQ state, you can step your way, statement by statement, through a protocol stream by using the command

You may issue this command repeatedly.

The content of static or dynamic variable groups may be displayed by using the commands

The BRIEF option on the SHOW STATIC VARIABLES command provides the same information as the SHOW STATIC VARIABLES command except that a series of zero-valued or null-valued variables in an array is condensed for display to only the first and last in the series, separated by a set of dashes to indicate those variables left off the display.

The following command shows the names of all allocated VGROUPS.

Having displayed the contents of appropriate variables, you may modify them by using LET commands. After this, if further execution is possible, you may issue the command:

to resume processing at the command where the error occurred.

A programmer may want to free the memory space held by a specific user-declared variable to prevent values of variables in one protocol from being used as starting values of the same variable in another protocol or simply to return memory to the computer. Once memory is allocated for dynamic variables, this space remains in use until the SPIRES session ends. The programmer may free the space taken by individual dynamic variables by using the $ZAP function:

clears the dynamic variable M from memory. Global clears are available for dynamic and static variables:

The global dynamic clear should be used with care, since a user may have his own dynamic variables in core when a protocol issues a global clear, thus erasing all of the user's dynamic variables in core.

8.3  Ascertaining the XEQ Level

The current position in a hierarchy of nested XEQ protocols can be displayed with a command of the form:

8.4  Protocol and Command Execution Tracing

For debugging protocols, SPIRES provides two different but related tracing facilities that show you the progress (or lack thereof) of a protocol as it executes:

Since SET ECHO was described earlier in this manual, this section will introduce SET XTRACE. [See 8.4.1.] Additionally, a logging facility, TLOG, that lets you send the SET ECHO data or SET XTRACE data or both to a log (and away from the terminal screen) will be discussed. [See 8.4.2.]

8.4.1  Protocol Tracing: SET XTRACE

SET XTRACE is the basic command for activating protocol tracing, where SPIRES displays environmental information about the protocol being executed. Depending on the options selected, SPIRES can show you when the protocol or any nested procs or protocols begin and end execution, the new values of any or specific user variables when they change, and what label group any JUMP or GOTO command invoked. Additionally, when you are debugging complex protocols that invoke other protocols, you can ask for tracing only for specific protocols.

There are four flavors of the SET XTRACE command, depending on which options you want to use. You may issue multiple SET XTRACE commands to achieve particular combinations.

The basic command is:

With this in effect, SPIRES will display an "-Entering: <name>" and "-Leaving: <name>" line for each protocol and proc (invoked by the XEQ PROC command) as execution of it begins and ends. The display will be indented appropriately to indicate the nesting of the protocols and procedures. Procs will be distinguishable from protocols by the "++" prefix (i.e., the label indicator) in front of their names.

To request the basic XTRACE information plus "jump" information, use:

Each time a JUMP or GOTO command is executed, SPIRES will display a "-Jump to <labelname>" line.

To request the basic XTRACE information plus a trace line whenever a specific or any user variable is changed, add the VARIABLES option:

If no "vars list" option is specified, SPIRES will display a "varname = value" line each time any user variable changes during protocol execution. For example, if variable #Redisplay is set to "0", the line "Redisplay = 0" will appear in the trace information.

But you can ask for only specific variables to be displayed by naming them in the optional variables list -- note that you omit the preceding pound sign when specifying them here. You can also begin by adding an "exclusion list", listing the variables you don't want to see by preceding them with a minus sign; in other words, all user variables except those named in the exclusion list will be included in the trace list.

Once you have set the VARIABLES option, you can change the list of variables by reissuing the SET XTRACE command with an exclusion list (preceded by a minus sign) or an inclusion list (preceded by a plus sign). The named variables will then be removed or added from the tracing list as appropriate.

The fourth flavor of SET XTRACE requests the basic tracing and lets you limit the tracing to only specific protocols being executed:

where "protocols" is a list of protocols that you want to include or exclude from the tracing. The first time you use the PROTOCOLS option in a given XTRACE session, you specify either an inclusion list ("protocols"), the protocols for which you want tracing, or an exclusion list ("- protocols"), the protocols for which you don't want tracing. Later, you can change the list of protocols being traced by adding new ones ("+ protocols") or removing others ("- protocols").

A convenient shortcut for setting all the options at once is:

This is exactly the same as issuing the commands SET XTRACE JUMP, SET XTRACE VARIABLES and SET XTRACE PROTOCOLS in a row.

Turning off the tracing is as simple as:

CLEAR XTRACE and CLEAR XTRACE ALL are equivalent, turning off all the tracing. However, you can turn off the individual options as well:

In the example below, protocol AAA calls protocol BBB, which in turn calls proc CCC, which calls protocol DDD.

The example shows the basic tracing data ("Entering" and "Leaving" lines, with information shown for the variable STATUS and for JUMPs as requested with the VARIABLE and JUMP options. Continuing this example, the tracing will look different when limited only to a single protocol:

SET XTRACE is often used in combination with SET ECHO, which displays the protocol commands as they are executed [See 5.6.] and with SET TLOG, which directs the tracing data to a temporary log file, which can be examined with the SHOW TLOG DATA command. [See 8.4.2.]

You can see what SET XTRACE commands are in effect with the SHOW XTRACE command:

Since SHOW XTRACE lists the actual commands, you can use the IN ACTIVE command to put them into your active file, where you can modify them and then re-execute them with the XEQ command. [See 2.1.] The $XTRACE variable can also be checked to determine what xtrace options are in effect.

8.4.2  Logging for Protocol and Format Tracing: SET TLOG

By default, the three commands that produce tracing information for protocols and formats, SET ECHO, SET XTRACE and SET FTRACE, display their information to the terminal screen while the protocol or format is executing. Sometimes, however, it's more convenient to send the tracing data elsewhere, both so that it can be captured and studied, and so that it doesn't get mixed up with whatever the format or protocol is sending to the terminal screen. The "somewhere" that SPIRES provides is called the "trace log", or "tlog" for short.

When you use the SET TLOG command to activate the trace log, SPIRES will send the trace output and, optionally, any error messages to the log. Later, you use the SHOW TLOG DATA command, perhaps with the IN ACTIVE prefix, to examine the contents of the trace log.

The syntax of the SET TLOG command is:

By default, system messages will go to the trace log (at message-level 3), regardless of the SET MESSAGES setting. (Hence, messages will still go to the terminal screen as well, unless they've been turned off there.) If you don't want the system messages included in the log, add the NOMSG option. To change the MSG or NOMSG setting later, simply re-issue the SET TLOG command with the appropriate option.

To see the contents of the trace log, use the SHOW TLOG DATA command:

Strictly speaking, the word DATA is optional, but we will refer to the command as SHOW TLOG DATA for clarity.

The MSG option tells SPIRES to display only the system-messages data and not the other tracing information; the NOMSG option displays only the echo, xtrace or ftrace information that has been saved in the log, without any system messages. If neither MSG nor NOMSG is specified, the entire contents of the log are displayed. (Note that you cannot see any system-messages data if you have turned off its logging with the SET TLOG NOMSG command.)

The PURGE option can be added to discard the current contents of the trace log when the display is completed; all the contents will be discarded, even if MSG or NOMSG is specified.

If you want to put the log into your active file, use the standard IN ACTIVE prefix, with or without its options such as CLEAR or KEEP.

You can also clear the contents of the trace log with the command:

This command in effect discards the current trace log and prepares to start a new one. This has the same effect as the PURGE option on the SHOW TLOG DATA command.

On the other hand, to end the logging altogether:

This command not only discards the current trace log, but turns off the logging itself.

To direct custom messages to the log, try using the $ISSUEMSG function [See 7.4.8a.] as in this example:

For most tracing situations, $ISSUEMSG is preferable to using the "*" command, which will only appear on the terminal screen and never as part of the trace log.

8.4.3  Error Logging: SET ELOG

SPIRES can keep a log of error messages that arise, which has some similarities to the trace log described in the previous section. [See 8.4.2.] The error log is useful in situations where you may receive multiple errors -- for example, where a record is being added to a subfile and multiple Inproc errors occur. Without the log, the protocol controlling the transaction would have direct access only to information about the last error encountered, not all of them. But with the error log (or "elog") turned on, the protocol can use the $GETELOG function to retrieve all the error messages individually.

The commands you use to initialize and work with the error log are similar to those for the trace log; however, the two logs are completely separate.

When you use the SET ELOG command to activate the error log, SPIRES will send any subsequent serious or warning error messages to the log, including serious or warning messages created by the $ISSUEMSG function. Informational and unconditional messages do not go into the error log. (Unconditional messages always go to the assigned message area. See the explanation for $ISSUEMSG for a description of unconditional messages.)

The syntax of the SET ELOG command, which activates and initializes error logging, is simply:

To see the contents of the error log at any time, use the SHOW ELOG command:

The PURGE option can be added to discard the current contents of the error log when the display is completed.

To put the log into your active file, use the standard IN ACTIVE prefix, with or without its options such as CLEAR or KEEP.

You can also clear the contents of the error log with the command:

This command in effect discards the current error log and prepares to start a new one. This has the same effect as the PURGE option on the SHOW ELOG command.

On the other hand, to end the logging altogether:

This command not only discards the current error log, but turns off the logging itself.

While you can see the entire log with the SHOW ELOG command, programs more commonly use the $GETELOG function to access its data. This function can retrieve not only the individual pieces of the log shown with the SHOW ELOG command but also other related data stored in the log that doesn't appear under SHOW ELOG.

For information about the $GETELOG function, see chapter 7, or online, EXPLAIN $GETELOG FUNCTION.

Two other variables associated with error logging are $ELOGSCNT and $ELOGWCNT, which count the number of Serious and Warning errors currently captured in the log, respectively.

8.5  Protocol Testing in Command Mode

You can also test each of your protocol statements individually, by typing the command at the terminal. [This assumes that the command is allowed at command level -- some commands such as JUMP are not.] Testing commands singly can be easier for quick debugging than adding a protocol to your protocols file or issuing the XEQ command for an entire active file.

For instance, you can reformulate a syntactically faulty command on the spot, until it executes correctly:

The return of the command prompt ('->') signals that SPIRES found the syntax of the rewritten command acceptable.

9  Building a Protocols File

9.1  The PERFORM BUILD PROTOCOLS Command

The PERFORM BUILD PROTOCOLS command is an ideal way to generate a standard protocol file. After you issue the command, you are asked questions about the file's name and the accounts you wish to give access to the file. Once you have answered these questions, the utility generates a file definition for your protocols file, adds the definition to the FILEDEF subfile, and compiles the file for you.

The resulting file automatically uses the $PROTOCOL format which gives you a convenient method to search the file by subject and to print your protocol records using the PERFORM PRINT command.

PERFORM BUILD PROTOCOLS is described in detail along with other PERFORM utilities in "SPIRES Technical Notes". Below is a sample session in which user GQ.JPR builds a protocol file with file name GQ.JPR.TESTPROTOS. (The subfile name is TEST PROTOS.) The part of the session that compiles the file definition occurs automatically without the user having to issue those commands:

The generated protocol file uses the gen-file format $PROTOCOL, which conveniently handles both display requests and input requests such as ADD, UPDATE or ADDUPDATE, and adds formatting features to protocols printed out with the PERFORM PRINT command. See the manual "SPIRES Technical Notes" for full information about the $PROTOCOL format and how it interacts with PERFORM PRINT.

9.2  Using PUBLIC PROTOCOLS

The PUBLIC PROTOCOLS file, although less often used today than in the past, contains various protocols that are available for "public execution," so to speak. A current list of protocols in the PUBLIC PROTOCOLS file, as well as a brief description of their function, can be obtained from the SPIRES file called MASTERLIST. To get such a list, execute the following SPIRES commands:

If you would like to see the protocol commands that make up any protocol in the PUBLIC PROTOCOLS file, you may

If you would like an off-line listing of any of the public protocols,

You will then be prompted for the name of the protocol you want to be listed offline.

9.3  Formatting Protocols Code: The PFORMAT Command

The PFORMAT command (which is actually a WYLBUR command) reformats protocols code in your active file, providing a standard indention style. PFORMAT also checks for unmatched pairs of BEGINBLOCK...ENDBLOCK, REPEAT...UNTIL, and WHILE...ENDWHILE statements.

Labels begin in column 1, and code starts in column 3. Block structures are indicated by further indention (in increments of 3, by default) based on the depth of nesting. Command lines that are continued with the "\" character are also indented 3 spaces by default. An INDENT option lets you control the number of spaces used for this second level of indention. The INDENT value can be any number from 2 -- 16:

Comments will stay in column 1 if they start there; otherwise they are indented the same as command lines.

DECLARE statements in Prism protocols start in column 1, with subsequent options (such as ON EVENT or ON COMMAND declarations) starting in column 3.

The "/" preparse character is relocated to the column preceding the start of the command to which it applies. The "!" noecho character is relocated to column 1.

Here is an example of the standard formatting supplied by PFORMAT:

10  Compiling a Protocol

Application developers are urged to compile their frequently used and/or complex protocols. This chapter focuses on the procedures for compiling protocols and using the compiled versions. Most of the chapter describes and refers to a method of compilation which compiles the protocol directly from the SPIRES file in which it is stored. An older procedure, described at the end of this chapter, compiles the protocol from a meta-data record stored in the SYS PROTO subfile; that procedure, called the SYS PROTO method, is no longer recommended, but is described in this chapter for compatibility with older applications. This chapter ends with a section about converting from the SYS PROTO method to the newer one.

10.1  Compiling a Protocol: Overview

Benefits of Compiling a Protocol

Compiling a protocol provides you with two very solid benefits:

Thus, though you are never required to compile a protocol (as you are required to compile a format or file definition) you will nearly always benefit from compiling a protocol that supports a production application in Prism or SPIRES.

At what stage should a protocol be compiled? One guideline: in the early stages of a protocol's development, when it is being tested and frequently changed, you may find it easier to test in its uncompiled form. Later, when the protocol moves closer to production, you will probably want to compile it for the sake of efficiency.

Overview of Steps in Compiling a Protocol

There are two methods for compiling a protocol. The older way, called the SYS PROTO method, is described at the end of this chapter. [See 10.8.] The newer method, described in the first few sections of this chapter, is the recommended way; it offers several advantages over the older way, including:

Unless you are wedded to the old method because conversion of your application to the new method would be too laborious, you should use the new method. [See 10.9 for information about converting from the SYS PROTO method to the new form.]

Before taking the steps below, you should have created a protocols subfile of your own, probably using PERFORM BUILD PROTOCOLS. [See 9.1.] The protocol you wish to compile should be a record in this subfile and it should already be largely tested and debugged. [See 8.]

Assuming the above conditions have been met, take the following steps to compile a protocol:

If you are recompiling a protocol that you compiled previously, use the RECOMPILE command instead:

This step is discussed in more detail in the next section. [See 10.2.]

To use the compiled protocol, you need to tell SPIRES what subfile contains it (with the SET COMPXEQ command), and then you start the protocol's execution with the "XEQ FROM protocol" or "..protocol" command. [See 10.3.]

Compiling a Protocol: Example

The example below shows the user compiling a protocol named LOOK.FOR.CAT.LADIES that is stored in a protocol subfile named CAT NETWORK PROTOS.

Step 1: Select the subfile containing the protocol.

Note: This protocols subfile must have been built using the $PROTOCOL record-type. [See 10.9.]

Step 2: Compile the record.

Use the COMPILE command to compile the SYS PROTO record:

Compiled vs. Uncompiled Protocols

Roughly speaking, compiling a protocol causes a second version of the protocol, somewhat rewritten for efficiency, to be stored in a system file. But the original, uncompiled protocol continues to sit in your protocols subfile unchanged.

If you select the protocols subfile for COMPXEQ, then SPIRES will execute the compiled version of the protocol when you request it. However, if you select the protocols subfile for XEQ, then SPIRES will execute the uncompiled version of the protocol when you request it. If you have the subfile set for both XEQ and COMPXEQ, then SPIRES will use the uncompiled version. [See 2.2, 10.3, 10.6.]

Other Features of Compiled Protocols

In addition to describing compilation in more detail, this chapter demonstrates some other features of compiled protocols:

These sections will provide you with a more complete picture of compiled protocols.

10.2  Compiling Protocols

In order to compile a protocol, you must first select the protocols subfile where the protocol is stored. [The protocols subfile must have been built using the $PROTOCOL record-type. Use the PERFORM BUILD PROTOCOLS command to create one. [See 9.1 Almost all protocols subfiles except the oldest ones are built this way., 10.9.]]

Then issue the COMPILE command:

If the protocol has been compiled before, use the RECOMPILE command instead of the COMPILE command. If the protocol has any blanks in its name, put the name in quotation marks or apostrophes on the command:

To obtain statistics on the compiled protocol, you can append the STATISTICS option to the COMPILE or RECOMPILE command. [See 10.7.]

If all goes well with the compilation, you will receive the following informational messages:

At that point, you can issue the SET COMPXEQ command and go on to execute the compiled protocol. [See 10.3.]

Errors in Compiling a Protocol

There's always a chance, of course, that the protocol will fail to compile or will give rise to a warning message after you issue the COMPILE or RECOMPILE command. Make changes to the protocol as needed, update it in the protocols subfile, and try to compile it again.

If you are not sure what the error message means, try using the EXPLAIN command, or else check the appendix in the back of this manual.

The following warning message indicates that one of the internal tables that SPIRES creates is close to its maximum size:

See the later section on statistics for details. [See 10.7.]

Other possible causes of errors in compilation include duplicate label names and undefined label names (e.g., if the protocol includes a "JUMP label.name" statement but the "label.name" does not occur somewhere in the protocol).

In addition, the protocol would be rejected if it contained too many labels for the compiler's table, or if the protocol as a whole were too large to be compiled. In the latter case, you might need to break the protocol into parts, perhaps using the "IN protocol.name" suffix to the XEQ PROC command in order to coordinate the separate parts. [See 3.2.1.]

10.2.1  Zapping Protocols

When you want to get rid of the compiled form of a protocol, use the ZAP PROTOCOL command:

where "protocol-name" is the name of the protocol, and "protocol-subfile" is the name of the subfile where it is stored.

If you add the SOURCE option, SPIRES will discard not only the compiled version (which is actually stored in a system subfile called COMP PROTO) but also the source record for the protocol in the named protocol subfile.

So, for instance, to get rid of the compiled and source version of the protocol TEST.ADD.ADDRESS from the subfile JNK PROTOCOLS, you could issue the command:

10.3  Executing Compiled Protocols: The SET COMPXEQ Command

In order to use pre-compiled protocols, you need only issue a SET COMPXEQ command instead of the SET XEQ command. [See 2.2 for SET XEQ.]

A session using compiled protocols might look like this:

To execute your own compiled protocols, SELECT your protocol subfile and issue the SET COMPXEQ command, or issue the command "SET COMPXEQ subfile-name", where "subfile-name" is the name of the subfile you want to be your COMPXEQ subfile.

Setting Secondary COMPXEQ Subfiles

You can set "secondary" COMPXEQ subfiles as well:

This form of the command can only be issued if you have previously set a "primary" COMPXEQ subfile. Using the "+" operator will add another COMPXEQ subfile; using the "-" operator will remove the named subfile from the COMPXEQ list. You cannot use the "-" operator to eliminate the primary COMPXEQ subfile. (Note that "+" and "-" must be surrounded by one or more blanks.)

If no subfile is named in the command, the currently selected subfile (or currently attached goal record-type if no subfile is selected) will be added or deleted from the list of COMPXEQ subfiles.

If you have set one or more secondary COMPXEQ subfiles, when you issue an "XEQ FROM" or "..protocol" command, SPIRES will first look for the protocol in the XEQ subfile (if there is one). If it doesn't find the named protocol in the XEQ subfile, it looks in the primary COMPXEQ subfile and then in each of the secondary COMPXEQ subfiles, in the order in which they were set. [See 10.6.] An error condition is raised if the protocol cannot be found in any of these subfiles.

To eliminate all secondary COMPXEQ subfiles at once, you would reissue the "SET COMPXEQ" or "SET COMPXEQ subfile-name" command, in effect beginning over again. Alternatively. you can issue the SET NOCOMPXEQ command, which also clears all current COMPXEQ subfiles.

SHOW COMPXEQ and $COMPXEQ

The SHOW COMPXEQ command will tell you all the COMPXEQ subfiles that are currently set, starting with the primary. The IN ACTIVE option can be used to place the output of the SHOW COMPXEQ command into the active file. [See A.2.9.1.] The value of the primary COMPXEQ subfile is maintained as $COMPXEQ. However, the $COMPXEQ variable cannot be used to determine whether more than one COMPXEQ subfile has been set.

By default, the "PUBLIC PROTOCOLS" subfile is set as the COMPXEQ subfile when you enter SPIRES. You can use the SET COMPXEQ command to reset some other subfile as the COMPXEQ file.

10.4  Efficient Execution: Coding Strategies

Virtually any protocol will execute run more efficiently if it is compiled. In fact, if a protocol contains many compilable statements, it may be faster than an uncompiled protocol by more than an order of magnitude.

The following types of statements are compiled:

These commands compile even when they are preceded by the slash prefix (/).

Comment statements, preceded by a "-", are left out of compiled code. A NOECHO marker, "!", is stripped from any statements it precedes, since compiled protocols never echo in any case.

Several techniques can be observed to allow more code to be compiled, and to provide quicker execution time of compiled code.

M and N should be TYPE = INTEGER and X should be TYPE = STRING.

If X is declared TYPE = INTEGER, then the $INTEGER function is unnecessary and time consuming.

Use separate variables. If this is not possible, then the variable must be give a declaration of TYPE = DYNAMIC, which is the least efficient TYPE.

If there are no $ variables or functions, rewrite IF statements so that # variables are on the left of the relational operators. For example, the first pair of statements below should be rewritten as the second pair.

Although statements preceded by the slash prefix can be compiled, there will still be an efficiency loss in using the prefix where it isn't needed.

10.5  Variable Communication: Local and Global Vgroups

Several cautions must be observed when compiling protocols that access variables created or altered by other protocols and formats. These common variables must be declared in a global vgroup that is added to the VGROUPS subifle and compiled. [See 4.1.] The global vgroup must be declared in a DECLARE GLOBAL VGROUP command block in the compiled protocol. [Or the global vgroup may be named in an ALLOCATE statement in the SYS PROTO record for the compiled protocol, if you are using the SYS PROTO method of protocol compilation. [See 10.8.]]

Reminder: There are two types of vgroups: local and global. Global vgroups are defined in Vgroups records and compiled separately. Local vgroups are defined in a DECLARE VGROUP command block, in a SYS PROTO record (old way) or in a format definition, and are compiled when the protocol or format is compiled.

The variables in a local vgroup cannot be accessed outside the format or protocol in which they are defined. [That is, they cannot be accessed by other program components. You can work with them at a BREAK XEQ pause, or, for a format's local vgroup, anytime in command mode while the format is set.] They are allocated (exist in memory) only as long as the protocol is being executed or the format is SET. The ALLOCATE command in SPIRES cannot be used to allocate a local vgroup, and local vgroups allocated by a "SET FORMAT format-name" or "XEQ FROM compiled-protocol-name" command always define a new vgroup in core, even if a vgroup with the same name already exists. Local vgroup variables always disappear when you CLEAR FORMAT or terminate XEQ of a compiled protocol.

A compiled protocol calling another compiled protocol, or a compiled protocol calling a format, cannot pass variables defined in a local vgroup, even if the variables are given the same name in each. However, variables declared and compiled in global vgroups can be shared and accessed by formats and protocols.

In short, if the variables from one protocol are needed by other protocols, then

If a protocol and format must use and communicate the same variables, then the above rules should also be followed.

10.6  Executing Compiled and Non-Compiled Protocols Simultaneously

By using both the SET XEQ and SET COMPXEQ commands simultaneously, it is possible for compiled and non-compiled protocols to call each other. While this facility is invaluable for debugging, it could lead to confusion if a particular protocol exists in both the XEQ and COMPXEQ subfile. For this reason, it is suggested that the SET NOXEQ command be issued when only compiled protocols are to be executed, and SET NOCOMPXEQ be issued when only non-compiled protocols are to be executed.

For any XEQ FROM or .. command, whether issued interactively or within a protocol, SPIRES checks to see whether a record by that name has been preloaded (with the LOAD PROTOCOL command), and, if so, executes the loaded copy of the record. If no record with that name has been preloaded, SPIRES acts as follows:

If neither XEQ nor COMPXEQ are SET, or the protocol cannot be found in the subfile(s) examined, the XEQ FROM or .. command gives an error message to the user.

10.7  (*) Statistics for Compiled Protocols

For large protocols it can be useful to gather statistics on the amount of space that the different components of your protocol are taking up in their compiled form (i.e., when stored by SPIRES as internal tables). These statistics can serve as an advance warning when some component of your protocol (or perhaps the protocol as a whole) is close to exceeding the maximum size allowed.

To obtain statistics, append the STATISTICS option to your COMPILE or RECOMPILE command, as in the example below:

  -> select sys proto
  -> recompile gq.pub.buildjcl statistics
   Protocol Statistics for FASTADD.JCL
      268 of  4088 Bytes ( 6%) ++Label Table
      444 of  4088 Bytes (10%) ++Label Names
      112 of  4088 Bytes ( 2%) Long Strings
     1520 of  8184 Bytes (18%) Functions/Expressions
     9512 of 32768 Bytes (29%) Commands
       22 of 16384 Bytes ( 0%) Allocate Tables
        2 of 16834 Bytes ( 0%) Local Vgroups
    11880 of 65536 Bytes (18%) Overall Limit
  -Compiled protocol: FASTADD.JCL
  -Protocol compile finished

The STATISTIC option can be abbreviated to STAT.

To direct the statistics to your active file instead of to the terminal, use the IN ACTIVE prefix:

  -> in active compile gq.pub.buildjcl stat
  -Compiled protocol: BUILDJCL
  -Protocol compile finished

Warning Signals in the Statistics

An asterisk at the beginning of a line indicates a table that exceeds 90% of the maximum size allowed. For instance, if the table for commands were to reach 98% of its allowed size, the statistics for the "command" table would look as follows:

   *32310 of 32768 Bytes (98%) Commands

(SPIRES would issue this information in a warning message if you omitted the STATISTICS option.) Note that the "Overall Limit" is less than the sum of the tables above it -- you might exceed the overall limit even if the size of the other tables is within the allowed limit.

What happens when a table exceeds the maximum allowed? In the statistics display, the line is marked by an asterisk and the percentage is designated as (**%). In addition, depending on how hard and fast the table limit is, the compiled code may not execute as you intend it to. If your protocol is in danger of hitting a table limit, consider breaking it into multiple protocols to be executed by "chaining". [See 2.2, 2.2.1.]

For information on obtaining statistics while compiling a file definition or a format, see the manuals "SPIRES Formats" and "SPIRES File Definition". See 4.1.2 in this manual for information about statistics for compiled vgroups.

10.8  Compiling a Protocol the Old Way

The method described in the first few sections of Chapter 10 [See 10.1, 10.2.] is the preferred way of compiling a protocol. Another older way, which is more trouble to do but which may be easier to continue using if you are already using it, is described in this section. It is still included in this chapter only for compatibility with older applications; it offers no benefits over the newer method, so you should not use this method if you are starting anew.

Whichever method you use, SPIRES will create the same compiled code, which it places into its COMP PROTO subfile. Hence, when you execute a compiled protocol, it makes no difference which method was used to create it; you don't set anything differently to use protocols compiled with one method vs. the other.

Overview of Steps in Compiling a Protocol

Here are the steps for compiling a protocol the old way:

Compiling a Protocol: Example

The example below shows the user GQ.JPR compiling a protocol named CHECKACCT that is stored in a protocol subfile named TEST PROTOS. (The filename of this protocol subfile is GQ.JPR.TESTPROTOS.)

Step 1: Create the description record and add it to SYS PROTO.

The record would look something like this:

Then add the record to the SYS PROTO subfile as follows:

Step 2: Compile the record.

Use the COMPILE command to compile the SYS PROTO record:

Step 3: Use the compiled protocol.

To use the compiled protocol, you need to tell SPIRES what subfile contains it (with the SET COMPXEQ command), and then you start the protocol's execution with the "XEQ FROM protocol" or "..protocol" command.

The First Two Steps in Detail

The first two steps are described in a little more detail below.

Step 1: Create the description record and add it to SYS PROTO.

To compile a protocol, you must first add a record "defining" that protocol and its variables to a public subfile called SYS PROTO. The basic structure of a record in the SYS PROTO subfile is as follows:

   ID = gg.uuu.anyname;
   FILE = gg.uuu.source-file-name;                      (*)
   RECORD = record-name-in-source-file;
   VGROUP = local-variable-group-name;
     VARIABLE = variable-name;
        TYPE = variable-type;
     VARIABLE = variable-name;
        TYPE = variable-type;
        OCC = subscripted-variable-occurrence-count;
        LEN = length-of-non-defaulted-variable;
   PROTOCOL = protocol-name;
         ALLOCATE = local-variable-group-name;
         ALLOCATE = global-variable-group-name;

Note: (*) the FILE element names the protocol subfile where the protocol is stored -- generally you will be the file owner of that subfile, except for Prism protocols, which are stored in a system subfile.

A record such as this must be added to SYS PROTO for each protocol or group of protocols (see note 3 below) that is to be compiled.

When defining the SYS PROTO record, note the following:

Once you have written the SYS PROTO record, select SYS PROTO in SPIRES and issue the ADD command to add it to the subfile.

Step 2: Compile the record.

To compile the protocol, you must give the name (ID) of the SYS PROTO record that defines the protocol in a COMPILE command:

If the protocol has been compiled before, use the RECOMPILE command instead of the COMPILE command.

To obtain statistics on the compiled protocol, you can append the STATISTICS option to the COMPILE or RECOMPILE command. [See 10.7.]

If all goes well with the compilation, you will receive the following informational messages:

At that point, you can issue the SET COMPXEQ command and go on to execute the compiled protocol.

If you get compilation errors, you will need to make changes to either the protocol or the SYS PROTO record or both. If you need more information about the messages, use the EXPLAIN command. [See 10.2 for a discussion of some common compilation errors.]

10.9  Converting from the Old to the New Way of Compiling Protocols

The older method of compiling protocols, described in the previous section, offers no advantages to the newer method. However, the newer method does provide some benefits, such as better checking of variable definition and use, and simpler metadata management (that is, no SYS PROTO records to deal with).

If you do decide to switch to the new method from the old, be aware that you do not have to convert all your old compiled protocols that work fine; they will continue to do so. Whichever method you use, SPIRES will create the same compiled code and store it internally in the same place.

But when the new method was introduced at Stanford, almost all the large applications (each with dozens of protocols) took some time (basically no more than a day's worth of effort) to convert their protocols to the new way. This was worthwhile because the programmers in each application area wanted a single method for compiling their protocols, and the new method's superiority made it the way they wanted to work in the future.

If you do decide to convert the protocols of an application from the old method to the new, you should follow these steps, described in detail below:

Step 1: Convert your Protocols Subfile

If you used PERFORM BUILD PROTOCOLS to create your protocols file, all you need to do is recompile its file definition in order to use the new method. Changes to the $PROTOCOL record definition were made in order for the new compilation method to work, so your file definition must be recompiled to use the new $PROTOCOL characteristics. (If you created your protocols file with PERFORM BUILD PROTOCOLS after the new method of compilation was announced, then you can skip this step. If you created your protocols file in some other way, you should consider using PERFORM BUILD PROTOCOLS to create a new protocols file; otherwise, you won't be able to take advantage of the new method.)

First, find the name of the file holding your protocols subfile, if you don't already know it. To do this, you can select the protocols subfile and issue the command SHOW SUBFILE INFORMATION.

Then, select the FILEDEF subfile, and issue the RECOMPILE command, naming the file:

Your protocols file is now ready for the new compilation method.

If you don't do this step, then when you try to compile a protocol with the new method, you'll get an error like this:

If you see this message, return to this step and verify all the points mentioned here.

Step 2: Convert old compiled protocols to the new method.

Step 2i: Take care of the variables and vgroups.

Under the new method of compilation, all user variables in a protocol should be declared in either a local vgroup defined at the start of the protocol:

The DECLARE VGROUP and DECLARE GLOBAL VGROUPS constructs are discussed elsewhere in this manual. [See 4.1, 4.1.0.]

Although you can create a dynamic element anytime you like in a compiled protocol, you will receive an error message when you compile; in general, all variables should be declared as part of a local or global vgroup, for better variable management. You can, of course, pre-define a dynamic element as shown in the example above. [See 4.4.]

In protocols compiled with the old method, the local and global vgroup declarations appear in the SYS PROTO record. To use the new method, you need to move that information from the SYS PROTO record into the protocol.

A system utility called PERFORM COMPPROTO can help you make these changes. Not only will it copy the vgroup data from the SYS PROTO record into the source record, but it will also zap the SYS PROTO record in its compiled and source forms from the appropriate system files (see step 2ii). Note that it adds a copy of the SYS PROTO record at the end of the protocol in the form of comments; eventually you will want to get rid of that, but at first, it may make you feel more comfortable to have a copy of the record around.

-> sel jnk protocols
-> perform compproto
Protocol compilation converter, version 93.12

This utility converts a protocol compiled via SYS PROTO to one that
can be directly compiled from the source.  It copies variable
information into DECLARE VGROUP statements at the top of your
protocol and ZAPs the SYS PROTO record.

-OK to clear? ok

Protocol name? test.add.address

SYS PROTO record key? (return=GQ.JNK.TEST.ADD.ADDRESS)
-Copying information from SYS PROTO record GQ.JNK.TEST.ADD.ADDRESS

-Zapping SYS PROTO record GQ.JNK.TEST.ADD.ADDRESS

Your converted protocol is in your active file.  The old SYS PROTO
has been saved as comments at the end of the protocol.  Next:

    - Review the code for accuracy
    - Issue the UPDATE command
    - Issue the COMPILE TEST command

Good luck!
-> list
    1.     * TEST.ADD.ADDRESS
    2.     declare global vgroups
    3.       allocate ORV.GQ.JNK.TEST.ADDRESS.VARS;
    4.     enddeclare
    5.     declare vgroup LOCAL
    6.       var Input;           Type string;    Occ = 3;
    7.       var Outside.Input;   Type dynamic;
    8.     enddeclare
    9.
           ... [the protocol commands]
  416.
  417.     - Protocol converted from SYS PROTO compile to direct compile
  418.     - by PERFORM COMPPROTO on 02/26/94 at 13:58:52.
  419.     - Old SYS PROTO record:
  420.
  421.     -  ID = GQ.JNK.TEST.ADD.ADDRESS;
  422.     -  MODDATE = SAT. FEB. 26, 1994;
  423.     -  DEFDATE = WED. JAN. 18, 1984;
  424.     -  FILE = GQ.JNK.PROTOCOLS;
  425.     -  RECORD-NAME = PROTO;
  426.     -  VGROUP = LOCAL;
  427.     -    VARIABLE = INPUT;
  428.     -      TYPE = STRING; OCC = 3;
  429.     -    VARIABLE = OUTSIDE.INPUT;
  430.     -      TYPE = DYNAMIC;
  431.     -  PROTOCOL = TEST;
  432.     -    ALLOCATE = ORV.GQ.JNK.TEST.ADDRESS.VARS;
  433.     -    ALLOCATE = LOCAL;
-> addupdate
->

Step 2ii: ZAP your old SYS PROTO record

You should get rid of both the compiled and source SYS PROTO records created for the old method before you compile with the new method. The PERFORM COMPPROTO command will take care of that for you.

If you do it yourself, the best way is to simply issue the ZAP SYS PROTO command with the SOURCE option:

Step 2iii: Compile your protocol.

Next, select the protocols subfile where you store the protocol, and issue the COMPILE command:

If you have any problems, you may need to make changes to the protocol, put it back in the subfile (UPDATE) and try compiling again.

Step 3: Use the new method for new protocols

The new method is described in detail earlier in this chapter. [See 10.2.] Be sure to use the DECLARE VGROUP and DECLARE GLOBAL VGROUP constructs when your protocol uses variables. [See 4.1, 4.1.0.]

11  Protocol Examples

NOTE: In the following examples, the line numbers in parentheses are correlated with line numbers in the comments after each example. The numbers are not part of a protocol command stream.

11.1  (PERFORM)

(1)   * PERFORM
(2)   ! SET MES = 0
(3)   !SET NOSTOP
(4)   !LET GOOD = $FALSE
(5)   !/$ASK
(6)   !IF $YES: LET GOOD = $TRUE
(7)   !SET STOP
(8)   ! SET MES = 2
(9)   !RETURN
(10)  !-
(11)  !- AUTHOR: L.E. ROSEN
(12)  !- This protocol will execute a SPIRES command passed in $ASK, and
(13)  !- will set GOOD to true or false, depending upon the success of
(14)  !- the command.
Line    Comments

2-3     Suppress all system messages and suppress BREAK XEQ on command failure
5       Execute the command passed in the parameters list to $ASK
6       If successful, alter value of GOOD assigned in 4
7-8     Reset system messages and automatic BREAK XEQ to sign-on default
9       Return to calling protocol (or command level)

11.2  (PAGINATE)

(1)   * PAGINATE
(2)   *
(3)   * If you have any carriage control characters in your data set,
(4)   * they will be moved to column 2.  You may modify them at 'X-?'.
(5)   * In response to the "X-?" prompt:  set length to the length of
(6)   * your longest line (approximately) and get data set into active file.
(7)   *
(8)   ! BREAK XEQ
(9)   ! LET '$$ECHO' = ''
(10)  ! IF $NOECHO : LET '$$ECHO' = NO
(11)  !SET NOECHO
(12)  - AUTHOR: J R Sack, Polya 162
(13)  - DESCRIPTION: inserts carriage control for new page at a
(14)  - line user chooses from a range the protocol lists.  Also
(15)  - inserts an incremented page number at the foot of the page.
(16)  ASK PRO='Maximum Lines per page (must be < 60)? '
(17)  /LET LINEEND = $INT($ASK)
(18)  /LET LINESTART = $ASK - 6
(19)  CHANGE 1 TO ' ' IN ALL NOLIST
(20)  SET WDSR (1)
(21)  WDSR
(22)  LET FIRSTLINE = $WDSR
(23)  LET PAGENO = 0
(24)  ++NEXT.PAGE
(25)  /SET WDST (#LINESTART) #FIRSTLINE / LAST
(26)  LET START = $WDST
(27)  /SET WDST (#LINEEND) #FIRSTLINE / LAST
(28)  LET PAGENO = #PAGENO + 1
(29)  IF $WDST < #LINEEND : JUMP EXIT
(30)  /LIST #START / $WDST
(31)  ASK PRO='First line of new page? '
(32)  LET FIRSTLINE = $LINE($ASK)
(33)  /LET WDSW = #FIRSTLINE -.002
(34)  /SET WDSW = #WDSW
(35)  /WDSW #PAGENO
(36)  /CENTER $WDSW LENGTH=$LENGTH
(37)  /CHANGE 1/1 TO '7' IN $WDSW NOLIST
(38)  /LET WDSW = #FIRSTLINE - .001
(39)  /SET WDSW = #WDSW
(40)  WDSW 1
(41)  JUMP NEXT.PAGE
(42)  ++EXIT
(43)  /WDS #PAGENO
(44)  /CENTER $WDSW LENGTH=$LENGTH
(45)  /CHANGE 1/1 TO '7' IN $WDSW NOLIST
(46)  *
(47)  /* Your active file has #PAGENO pages.
(48)  * Be sure to use 'CC' in the list offline options.
(49)  *
(50)  /SET #'$$ECHO'ECHO
Line    Comments

2-7     Type messages to terminal
8       Return control to user to access the text to be paginated
9-11    Save entry value of $ECHO, then reset $ECHO
17-18   Set a linerange between which the page may be broken
20-23   Initialize several variables
25-26   Find the first line of the range in which a page break can occur
27      Find the last line of the range in which a page break can occur
28      Increment the page number counter
29      If less than a page is left, jump to a concluding procedure
32      Convert the string response to a line variable
33-37   Insert a skip to bottom line with page number centered in it
38-40   Insert a line with a skip to new page carriage control
43-49   Put in last page number and exit protocol
50      Reset user's $ECHO setting

11.3  (CLEAR.TEXT)

(1)   * CLEAR.TEXT
(2)   ! SET WDST = LAST
(3)   ! IF $WDST < 0 : JUMP EXIT
(4)   ! IF $CLEAR : JUMP CLEARTEXT
(5)   ! ASK UPPER PRO='CLEAR TEXT?' ATTN='JUMP XEQ.ABORT'
(6)   ! LET M = $MATCH($ASK,YES,YE,Y,OK,O)
(7)   ! IF #M >= 1 : JUMP CLEARTEXT
(8)   ! ++XEQ.ABORT
(9)   *
(10)  * XEQ ABORTED.  DO NOT DO THIS PROTOCOL UNTIL THE ACTIVE
(11)  * FILE CAN BE CLEARED.  YOU WILL BE RETURNED TO SPIRES.
(12)  *
(13)  ! ..RESTORE.SETTINGS
(14)  ! CLEAR XEQS
(15)  ! ++CLEARTEXT
(16)  ! CLEAR TEXT
(17)  ! ++EXIT
(18)  ! SET CLEAR
(19)  ! RETURN
(20)  - Author: J R Sack, Polya 162
(21)  - Description: asks if a clear text can be executed; if yes,
(22)  - then the protocol clears text; if no, the user is returned
(23)  - to SPIRES command mode after an error message.
(24)  - "..save.settings" must be xeq before a "..clear.text"
Line    Comments

2-3     If no active file, don't ask to clear it
4       If user had set $CLEAR, automatically clear text
5-7     Jump if a response matches
8-14    Abort XEQ and return to command mode if match fails
15-19   Clear text, set auto clear, return one level
24      If XEQ is aborted, then RESTORE.SETTINGS will be executed,
              this protocol uses variables defined by SAVE.SETTINGS;
              the calling protocol must issue a RESTORE.SETTINGS of
              its own to reset $CLEAR to its sign on value

11.4  (LIST.PROTOCOL)

(1)   * LIST.PROTOCOL
(2)   ! ..SAVE.SETTINGS
(3)   ! SET NOECHO NOLIST
(4)   ! ..CLEAR.TEXT
(5)   /SELECT $XEQ
(6)   ++ASK
(7)   ASK UPPER PRO='WHICH PROTOCOL?' ATTN='JUMP XEQ.ABORT'
(8)   /..PERFORM TRANSFER $ASK
(9)   IF #GOOD: JUMP TRANSFER.OK
(10)  *
(11)  /* '$KEY' is not a protocol in '$XEQ'
(12)  *
(13)  JUMP ASK
(14)  ++TRANSFER.OK
(15)  /LIST OFFLINE UPLOW '*** SPIRES PROTOCOL ($DATE at $TIME) ***'
(16)  CLEAR TEXT
(17)  ++XEQ.ABORT
(18)  !..RESTORE.SETTINGS
Line    Comments

2       XEQ a protocol that saves all of a user's settings
3       Alter settings
4       XEQ a protocol that prompts if clear text is OK
5       SELECT The subfile for which XEQ is SET
7       Prompt for protocol name
8       Attempt to transfer the protocol name given using PERFORM
9-13    If transfer fails print an error message and reprompt
14-15   List the transferred protocol offline
16      Clean up the active file
17-18   Restore the user's settings to their value before XEQ

11.5  (SHOW.FILE.PERMITS)

(1)   * SHOW.FILE.PERMITS
(2)   ! ..SAVE.SETTINGS
(3)   ! SET WARN MES = 0
(4)   ! SET WDST = 'ORVYL FILE SYSTEM' 1
(5)   ! IF $WDST > 0 : ..CLEAR.TEXT
(6)   ! DUMP FILES
(7)   ! SET WDSR = 'ORVYL FILE SYSTEM' 1
(8)   ! / SET WDST = $WDSR
(9)   ! WDSR
(10)  ! ++LOOP
(11)  ! WDSR END='JUMP EXIT'
(12)  ! / SHOW PERMITS $ASK
(13)  ! JUMP LOOP
(14)  ! ++EXIT
(15)  ! / DELETE $WDST/LAST
(16)  ! ..RESTORE.SETTINGS
(17)  ! RETURN
(18)  - THIS PROTOCOL SHOWS FILE PERMITS ON ALL THE USERS
(19)  - ORVYL FILES
(20)  - AUTHOR: JOHN R. SACK, USER SERVICES, POLYA 162
Line    Comments

2       Save user's settings
3       Cancel warning messages caused by protocol XEQ
4       See if this string is in the active file in column one
5       If yes, then execute the CLEAR.TEXT protocol
6       If no, add ORVYL file names to end of the active file
              using the ORVYL 'DUMP' command
7       Save the line at which the dump began in $NEXTWDSR and then
              save it in $WDST
9       Read the line whose number is stored in $NEXTWDSR; this
              line number was set by the SET WDSR in 7
11      Read each line into $ASK following the first line dumped
12      Execute a SHOW FILE PERMITS command for each file name
              stored in $ASK by the preceding WDSR command

11.6  (SAVE.SETTINGS)

(1)   * SAVE.SETTINGS
(2)   !LET '$INFOX' = $INFOX
(3)   !LET '$WARNX' = $WARNX
(4)   !LET '$SERX' = $SERX
(5)   !LET '$ECHO' = ''
(6)   !LET '$LIST' = ''
(7)   !LET '$STOP' = ''
(8)   !LET '$DIAG' = ''
(9)   !LET '$CLR' = ''
(10)  !LET '$CASE' = UPLOW
(11)  !LET '$LEN' = $STR($LENGTH)
(12)  !IF $NOECHO THEN LET '$ECHO' = NO
(13)  !IF $NOLIST THEN LET '$LIST' = NO
(14)  !IF $NOSTOP THEN LET '$STOP' = NO
(15)  !IF $NODIAG THEN LET '$DIAG' = NO
(16)  !IF $NOCLR THEN LET '$CLR' = NO
(17)  !IF $UPPER THEN LET '$CASE' = UPPER
(18)  !LET '$SELECT' = $SELECT
(19)  !LET '$FORMAT' = $FORMAT
(20)  !RETURN
(21)  - Dick Guertin, SCIP

[See 11.7 for comments.]

11.7  (RESTORE.SETTINGS)

 (22) * RESTORE.SETTINGS
 (23) !/SET LENGTH #'$LEN'
 (24) !/SET #'$CASE'
 (25) !/SET #'$ECHO'ECHO #'$LIST'LIST #'$STOP'STOP #'$DIAG'DIAG #'$CLR'CLR
 (26) !/SET INFO MES #'$INFOX'
 (27) !/SET WARN MES #'$WARNX'
 (28) !/SET SER MES #'$SERX'
 (29) !IF $SELECT = #'$SELECT' THEN GOTO FORM
 (30) !IF $SELECT > ' ' THEN CLEAR SELECT
 (31) !++FORM
 (32) !IF #'$SELECT' <= ' ' THEN RETURN
 (33) !/SELECT #'$SELECT'
 (34) !IF #'$FORMAT' > ' ' THEN /SET FOR #'$FORMAT'
 (35) !RETURN
 (36) -- Dick Guertin, SCIP
Line    Comments

2-4     System defined variables are saved in dynamic variables,
              which must be enclosed in apostrophes because they
              begin with a special character, '$'
5-11    Dynamic variables that may be left over from a previous
              SAVE or RESTORE.SETTINGS are initialized
12-17   String prefixes are assigned for certain flag variables;
              these prefixes are reset in RESTORE.SETTINGS line 25
23-34   System defined variables are reset; note that the variables'
              names all begin with '$' to insure that they are
              different from any variables a user may himself have
              dynamically defined.  The value of a variable whose
              name begins with '$' must appear enclosed in
              apostrophes and prefixed by a '#' (the '#' is
              required for all uses of a variable's value).
              #'variable.name' is the form when variable.name
              contains special characters such as '$'.

11.8  (PAGE.TO.SECTION)

(1)   * PAGE.TO.SECTION
(2)   ! SET NOECHO
(3)   ++LOOP
(4)   LET TOP = $NEXTWDSR
(5)   / SET WDSR 'page-XX'  IN #TOP/LAST
(6)   IF ~$NEXTWDSR : RETURN
(7)   / LIST $NEXTWDSR
(8)   ASK PRO='Sec '
(9)   LET SECTION = $ASK
(10)  / CHANGE 'page-XX' to 'Sec #SECTION ' in $NEXTWDSR
(11)  JUMP LOOP
Line    Comments

4       Begin the line range with the last line found, in case the
              string being search ed occurs more than once per line
5       Search for the first line with the string 'page-XX' in
              the indicated range
6       If a line is not found, $NEXTWDSR is 0 or FALSE, then
              return to next higher level, usually command mode
7       List the line found; the line number found by a SET WDSR command
              is stored in $NEXTWDSR until a WDSR command is done
8-9     Prompt for the string that 'page-XX' is to be changed
              to in line 10
10      Execute a change command for the line
11      Jump to search for another line

12  Protocol Exercises

The following set of exercises will provide a helping hand through some of the elementary steps in building and maintaining protocols under SPIRES, especially for those who are unable to attend the User Services short courses on SPIRES but do have access to a terminal.

The exercises will give you experience with the commands and concepts discussed in Sections 2 and 3 of the SPIRES/370 Protocols manual. In addition you will become familiar with the procedures necessary to:

Note: Section numbers in parentheses refer to the SPIRES/370 Protocols manual.

These exercises were written by Carol Lennox.

12.1  (Using SPIRES system variables in a command)

PART A:
  Logon to the system and call SPIRES.   Type  in  the  following
  command:

    -> SHOW LINE OPERATOR

  Now type in the following two commands:

    -> LET X = OPERATOR
    -> / SHOW LINE #X
Q1:
  Did you get the same response from the system for both sets  of
  commands?
Q2:
  What would happen if you did not include the  '/'  substitution
  prefix  at  the  front of the second line?  If you are not sure
  try the command :

    -> SHOW LINE #X
PART B:
  Now try the following command:

    -> / SHOW LINE $TERMINAL
Q1:
  What line did the system give you a response for?
Q2:
  Would you get the same information if you did not  include  the
  '/' substitution prefix?

12.2  (Executing a protocol stored in the active file)

PART A:
  Instead of entering each command directly from the terminal, as
  in exercise 1, let's now collect the same four commands in  the
  active  file  and execute them as a SPIRES protocol.  Logon and
  call SPIRES then type in the following commands and data:

    -> CLEAR ACTIVE
    -> COLLECT
        1.   > SET ECHO
        2.   > SHOW LINE OPERATOR
        3.   > LET X = OPERATOR
        4.   > / SHOW LINE #X
        5.   > / SHOW LINE $TERMINAL
        6.   > (attn)
    -> XEQ

  You have just executed the commands in the  active  file  as  a
  SPIRES protocol.
Q1:
  Did you get the same responses from the system as  in  exercise
  1?
Q2:
  Are the commands you stored in  the  active  file  still  there
  after  executing  them  as a protocol?  If you aren't sure, use
  the LIST command to see if anything is in the active file.
PART B:
  You notice that each command in  the  protocol  was  listed  by
  SPIRES at your terminal before it was executed.  This is called
  ECHO  mode  and  is  controlled  by the SET ECHO and SET NOECHO
  commands.  If you would like to  inhibit  the  listing  of  the
  commands,  change  line  1  in  the  protocol  as  follows  and
  re-execute the protocol:

    -> CHANGE 'ECHO' TO 'NOECHO' IN 1
    -> XEQ

  This is called NOECHO mode  and  causes  all  printing  of  the
  protocol  commands  to  be  suppressed  when  the  protocol  is
  executed.  Notice that the command SET NOECHO is itself echoed.
  To  prevent  this, simply put an "!" in front of the SET NOECHO
  command.
PART C:
  Now let's save the protocol collected in the active file  as  a
  member  of  your  WYLBUR  library  data set and then see how we
  could retrieve the protocol at  a  later  time  for  execution.
  First  we  will  have  to  save the protocol and then clear the
  active file by typing the following:

    -> SAVE #TEST1
    -> CLEAR ACTIVE

  The active file  is  now  empty.   In  order  to  retrieve  the
  protocol  from  your library and execute it, you need only type
  in the following two commands:

    -> USE #TEST1
    -> XEQ
Q1:
  Were the system responses the same as for part A ?
Q2:
  If you logon to the system tomorrow at  a  different  terminal,
  call  SPIRES  and  then "USE" and "XEQ" the protocol as you did
  above, would the system still give you the same  response?   If
  so, why?  If not, why not?

12.3  (Printing the values of variables at the terminal)

PART A:
  First let's simply try printing the current  value  of  several
  system  variables.   Logon to the system and  call SPIRES.  Now
  type in the following commands (the system  responses  are  not
  shown):

    -? / * $TERMINAL
    -? / * $DATE
    -? / * $TIME
    -? / * $LENGTH
    -? / * $NAME
Q1:
  Can you  verify  any  of  the  values  printed  in  the  system
  responses:  e.g., using WYLBUR commands like SHOW DATE and SHOW
  LINE?
Q2:
  Would the system responses change in any way  if  you  did  not
  type  a  "/"  substitution  prefix  at  the  beginning  of each
  command?  If you are not sure, try it.
PART B:
  Now let's establish a value for  a  user-defined  variable  and
  then  see  if we can cause the values to print at the terminal.
  Type in the following commands:

    -? LET X=10
    -? * #X
    -? / * #X
Q1:
  What is the difference in the system response between  lines  2
  and 3?
Q2:
  Which symbol in both lines 2 and  3  indicated  you  wanted  to
  refer to the value associated with the variable named X?
Q3:
  Why wasn't the value of X printed for the second command?   Did
  the  use  of the # symbol automatically cause the value of X to
  be substituted in the system response?
Q4:
  Which symbol in the third line actually forced  the  system  to
  substitute the value 10 for the symbol #X in the response?
PART C:
  Now let's build a protocol in the active file that  will  print
  both  system  and user-defined variable values at the terminal.
  Type in the following commands:

    -? SET NOECHO
    -? COLLECT  CLEAR
       1.   ? *
       2.   ? /* HELLO THERE, $NAME...
       3.   ? /* I SEE YOU ARE USING TERMINAL $TERMINAL TODAY
       4.   ? /* FOR YOUR RECORDS IT IS NOW $TIME AND THE DATE IS $DATE
       5.   ? *
       6.   ? * LET'S SEE HOW GOOD YOU ARE AT ARITHMETIC...
       7.   ? * FIRST LETS ADD THE NUMBERS 10 AND 50
       8.   ? LET SUM=10+50
       9.   ? /* I GET #SUM ...HOPE YOU DID TOO!
      10.   ? /* NOW LETS MULTIPLY #SUM BY 5
      11.   ? LET PRODUCT = #SUM*5
      12.   ? /* IF YOU DID IT PROPERLY; YOU GOT #PRODUCT
      13.   ? /* THAT'S ALL FOR NOW, $NAME ...BYE
      14.   ? *
      15.   ? (attn)
    -? XEQ
Q1:
  What did lines 1, 5 and 14 cause to happen at the terminal?
Q2:
  Can you explain what  the  slash  at  the  beginning  of  lines
  2, 3, 4, 9, 10, 12 and 13 does?
Q3:
  Now let's see what happens if we try to print the  value  of  a
  variable  which  has  not yet been assigned any explicit value.
  Type in the command:

      -?  / * #NOVAL

  Does the system print a "default" value for the NOVAL variable?
Q4:
  Does the system even seem to consider NOVAL a variable?

12.4  (Substitution versus Evaluation)

PART A:
  In the protocol in exercise 3 Part C at line 8, we used the LET
  command to compute or "evaluate" the expression 10+50.  In line
  11  we  again used the LET command to "evaluate" the expression
  "#SUM*5."   In  this  LET  command,  the  system  automatically
  substituted the value associated with the variable  SUM  before
  evaluating  the  expression.  So, we see three important things
  about the LET command:

    . Substitution of values  for  variable  names  preceded
      with  a  # or $ is done automatically, without the use
      of the "/" substituion prefix.

    . After   substitutions   have   been   performed,   the
      expression to the right of  the  =  sign  in  the  LET
      command is evaluated.

    . The result of the  evaluation  is  "assigned"  to  the
      variable named on the left of the = sign.

  Our question now is:  if we include  an  expression  in  the  *
  command,  such  as  10+50,  will  the  system  "evaluate"  that
  expression  and substitute the value 60 in the system response?
  Let's try the command:

    -? *  10+50
Q1:
  Does  the  system  automatically  evaluate  expressions  in   *
  commands?

  Our next question is:  Will the use  of  the  "/"  substitution
  prefix  cause  evaluation  of  an  expression in the * command?
  Let's try the command:

    -? / * 10+50
Q2:
  Did  the  substitution  prefix  cause  the  expression  to   be
  evaluated?
PART B:
  Now let's see if the use of user-defined variables  instead  of
  actual  numbers  results  in evaluation of expressions in the *
  command:

    -? LET X=10
    -? *  #X+50
    -? / *  #X+50
Q1:
  Did the system evaluate either of  the  expressions  in  the  *
  commands?

  Has the system evaluated  any  of  the  expressions  in  the  *
  commands we have tried so far?
Q2:
  In fact, SPIRES never  evaluates  expressions  contained  in  *
  commands.   Expressions  will only be evaluated when they occur
  in the LET, the IF and the EVAL commands.
Q3:
  Can you now describe the difference  between  substitution  and
  evaluation?
PART C:
  Now let's use the LET command to  cause  an  expression  to  be
  evaluated  and  the result assigned to a variable.  We can then
  print the resulting value using the *  command.   Type  in  the
  following commands:

    -? LET SUM=10+50
    -? /* THE SUM OF 10 and 50 is #SUM
Q1:
  If you want an expression evaluated, and the result printed  at
  the  terminal, what are the two commands we have studied so far
  that you have to use?
Q2:
  Now look back at the protocol we entered in exercise 3 Part  C.
  Examine  lines  8  and  9.   Can you now explain why we entered
  these two lines instead of a line like " /*  I  GET  10+50  ...
  HOPE YOU DID TOO!" ?

12.5  (Using the ASK command to prompt for information from the terminal)

PART A:
  First let's collect a very simple protocol in the  active  file
  which,  when  executed,  will  prompt you for your age and then
  print it back at the  terminal.   Logon  to  the  system,  call
  SPIRES, and then type in the following commands:

    -? SET NOECHO
    -? COLLECT CLEAR
        1.   ? ASK PROMPT='HOW OLD ARE YOU? '
        2.   ? /* HMMM .... $ASK IS PRETTY DARN OLD!
        3.   ? *
        4.   ? (attn)
    -? XEQ

  When you are asked "HOW OLD ARE YOU?", enter your age and  then
  press the carriage return.
Q1:
  To what system  variable  was  your  reply  from  the  terminal
  assigned?  If you are not sure type:
        -? /* $ASK

12.6  (Using the WDSW command)

PART A:
  Let's now try a protocol which uses the WDSW command  to  write
  several lines to the WYLBUR active file.  First we will collect
  the  protocol  in  the  active file and execute it, including a
  LIST command as part of the protocol.   Logon  to  the  system,
  call SPIRES, and type in the following commands:

    -? SET NOECHO
    -? COLLECT  CLEAR
        1.   ? CLEAR ACTIVE
        2.   ? WDSW  HELLO THERE
        3.   ? WDSW  THIS LINE IS IN THE ACTIVE FILE BECAUSE A SPIRES
        4.   ? WDSW  PROTOCOL PUT IT HERE USING A WDSW COMMAND
        5.   ? / WDSW  WELL, $NAME, THATS ALL
        6.   ? LIST UNN
        7.   ? *
        8.   ? *
        9.   ? * IF YOU DONT BELIEVE ME, TRY THE LIST COMMAND YOURSELF!
       10.   ? *
       11.   ? (attn)
    -? XEQ
Q1:
  Which command in the protocol  erased  the  protocol  from  the
  active file?
Q2:
  Which commands actually caused new information  to  be  written
  into the active file?

12.7  (Using the ASK command with label statements and JUMP commands)

PART A:
  Section 2.6 discussed the ASK command briefly.   It  showed  an
  example  of  an  ASK  command  that  contained JUMP commands to
  handle the situations where the  user  types  only  a  carriage
  return  or  an  attention in response to the prompt.  Let's now
  try a very simple protocol which will do different things  when
  executed,  depending on your response to the ASK prompt.  Logon
  and call SPIRES then type in the following commands:

    -? CLEAR ACTIVE
    -? COLLECT
        1.   ? !SET NOECHO
        2.   ? *
        3.   ? ASK PROMPT='NAME? ' NULL='JUMP SHY' ATTN='JUMP ITCHY'
        4.   ? /* WELL $ASK; YOU APPEAR TO BE A FRIENDLY SOUL.
        5.   ? JUMP QUIT
        6.   ? ++SHY
        7.   ? * HMM.. LOOKS LIKE YOU ARE RATHER SHY!
        8.   ? JUMP QUIT
        9.   ? ++ITCHY
       10.   ? * REALLY.. YOU DONT HAVE TO HURRY AWAY SO FAST!
       11.   ? ++QUIT
       12.   ? * BYE....
       13.   ? (attn)
    -?
PART B:
  Now let's execute the protocol four times.  The first time  try
  typing  in  your name when prompted; the second time try typing
  just  a  carriage  return;  the  third  time  try  hitting  the
  attention key; and the fourth time try typing a space  followed
  by a carriage return.
Q1:
  Did you get a different response each time?
PART C:
  Now let's assume we want to simply reprompt the person for  his
  name  if  he hits only the carriage return.  This is the action
  SPIRES will automatically take if the ASK command has  no  NULL
  option specified.  Change the protocol in 7 Part A as follows:

    -? REPLACE 3
        3.   ? ASK PROMPT='YOUR NAME PLEASE? ' ATTN='JUMP ITCHY'
    -? DELETE 6/8

  Now execute the protocol and see what happens  if  you  respond
  with  only  a  carriage  return.  Execute the protocol a second
  time, responding with a space followed by a carriage return.

12.8  (Using the IF command)

PART A:
  Let's build a protocol which uses the IF command.  The protocol
  will carry on a very simple conversation with whomever executes
  it, taking different actions depending on the answer typed  in.
  Logon  to  the  system,  call  SPIRES,  and  then  type  in the
  following commands:

    -? CLEAR ACTIVE
    -? COLLECT
        1.   ? !SET NOECHO
        2.   ? *
        3.   ? * I AM THINKING OF A NUMBER BETWEEN 1 AND 10...
        4.   ? ASK PROMPT='WHAT IS IT? '  NULL='JUMP QUIT' ATTN='JUMP QUIT'
        5.   ? IF $ASK = 3 THEN * WOW! YOU GOT IT!
        6.   ? IF $ELSE THEN * TOO BAD...YOU LOSE...IT WAS 3!
        7.   ? ++QUIT
        8.   ? * THATS ALL FOR NOW... BYE
        9.   ? *
       10.   ? (attn)
    -?
  Now execute the protocol several times, changing your  response
  each time, to see the effect of the IF statement.
PART B:
  Let's now make the protocol a  little  more  sophisticated  and
  include  some  error  checking  on  the  answer.   Type  in the
  following commands:

    -? SET NOECHO
    -? COLLECT CLEAR
        1.   ? *
        2.   ? * I AM THINKING OF A NUMBER BETWEEN 1 AND 10
        3.   ? ++QUESTION
        4.   ? ASK PROMPT='WHAT IS IT?' NULL='JUMP NO.GUESS' ATTN='JUMP QUITTER'
        5.   ? IF $INT($ASK) < 1 THEN JUMP TOO.LOW.HIGH
        6.   ? IF $INT($ASK) > 10 THEN JUMP TOO.LOW.HIGH
        7.   ? IF $INT($ASK) = 3 THEN JUMP WINNER
        7.1  ? - $INT tells SPIRES to treat the value as an integer
        7.2  ? -   rather than as a character string.
        8.   ? /* NOPE, $ASK ISNT IT!
        9.   ? JUMP QUESTION
       10.   ? ++NO.GUESS
       11.   ? * COME ON NOW; TAKE A GUESS!
       12.   ? JUMP QUESTION
       13.   ? ++WINNER
       14.   ? * WOW!!! YOU GOT IT!! CONGRATULATIONS!
       15.   ? JUMP QUIT
       16.   ? ++TOO.LOW.HIGH
       17.   ? * COME ON, THATS NOT BETWEEN 1 AND 10
       18.   ? JUMP QUESTION
       19.   ? ++QUITTER
       20.   ? * SORRY....THE NUMBER WAS 3
       21.   ? ++QUIT
       22.   ? * BYE....
       23.   ? (attn)
    -?

  Execute the protocol several times now, trying numbers like -1,
  28, 6 and just carriage return to see the different actions.
PART C:
  Let's further refine the protocol so that it limits the user to
  only two guesses.  We'll do this  by  establishing  a  variable
  which we will use to count the number of tries the person makes
  which  are  between 1 and 10 but not the number 3.  We will not
  count a carriage return only or a number outside the range 1 to
  10 as a try.  Modify the protocol to do this by typing  in  the
  following commands.

    -? INSERT 2.5
         2.5  ? LET TRYS=0
    -? INSERT 8.2, 8.4, 8.6
         8.2  ? LET TRYS = #TRYS+1
         8.4  ? IF #TRYS=2 THEN JUMP QUITTER
         8.6  ? * ONLY ONE MORE CHANCE
    -? LIST

  Study the protocol as it now stands and be sure you  understand
  what  the  four  inserted  commands  will cause to happen.  Now
  execute the protocol, entering numbers other than 3.
Q1:
  Can you figure out what changes and additions would have to  be
  made to the protocol so that it would allow up to 5 guesses?
Q2:
  Can you figure out what changes would be  needed  to  give  the
  person  a  hint as to the answer, such as 'HINT:  THE NUMBER IS
  LESS THAN 5' once he or she has guessed wrong three times?

12.9  (Using XEQ command options with a protocol in the active file)

PART A:
  Let's enter  a  protocol  in  the  active  file  and  then  try
  executing  "pieces"  of  it  by specifying the AT and the USING
  options of the XEQ command discussed in section 3.1.  Logon  to
  the system, call SPIRES and type the following:

    -? COLLECT CLEAR
        1.   ? !SET NOECHO
        2.   ? *
        3.   ? /*  HELLO $NAME
        4.   ? *   I AM GOING TO PRINT A TABLE OF SQUARES
        5.   ? *
        6.   ? ++ HEADING
        7.   ? *  NUMBER  SQUARE
        8.   ? LET N=1
        9.   ? ++LOOP
       10.   ? LET NSQR=#N*#N
       11.   ? /*    #N    #NSQR
       12.   ? LET N=#N+1
       13.   ? IF #N <= 9 THEN JUMP LOOP
       14.   ? *
       15.   ? (attn)
    -?

  Now let's try executing only  the  first  seven  lines  of  the
  protocol, to see if the greeting and table heading are properly
  spaced.  Type in:

    -? XEQ USING 1/7

  Now let's see if the table itself is generated properly.   Type
  in:

    -? XEQ USING 8/LAST

  Now let's see if the table heading and the  table  entries  are
  properly aligned.  Type in:

    -? XEQ AT HEADING
Q1:
  What would print at the terminal if you typed in the  following
  command?

    -? XEQ AT HEADING USING 4/7

  If you are not sure, try it.
Q2:
  What difference in printout at the terminal  would  you  expect
  from the following two commands?

    -? XEQ USING 8/LAST
    -? XEQ USING 9/LAST

12.10  (Choosing a protocol subfile)

PART A:
  Whenever you wish to invoke a protocol which has been stored in
  a SPIRES protocol subfile, you must do two things:

     .  SELECT the protocol subfile
     .  Use the SET XEQ command to establish it
        as the default protocol subfile from which
        future protocols are to be obtained.

  You can use the SHOW XEQ  command  at  any  time  to  determine
  whether   or  not  a  default  protocol  subfile  is  currently
  established.  Logon to the system, call SPIRES, and type in the
  following:

    -? SHOW XEQ
    -? SELECT PUBLIC PROTOCOLS
    -? SHOW XEQ
    -? SET XEQ
    -? SHOW XEQ
Q1:
  Was there a default protocol subfile established when you typed
  in the first SHOW XEQ command?
Q2?:
  Did the selection of the PUBLIC PROTOCOLS subfile automatically
  cause it to be established as the default protocol subfile?  If
  you are not sure, consider the system response  to  the  second
  SHOW XEQ command.
Q3:
  After selecting the  PUBLIC  PROTOCOL  subfile,  which  command
  finally  established  it  as the default subfile from which all
  future protocols will be obtained if an  XEQ  FROM  command  is
  given?
PART B:
  Now let's examine what happens  when  we  establish  a  default
  protocol  subfile  and  then  select another subfile we wish to
  search.  Type in:

    -? SELECT PUBLIC PROTOCOLS
    -? SET XEQ
    -? SHOW XEQ
    -? SELECT RESTAURANT
    -? SHOW FILE SIZE
    -? SHOW XEQ
Q1:
  What was the name of the second subfile we  selected,  and  how
  many records (restaurant descriptions) did it contain?
Q2:
  Did the selection and use of the RESTAURANT subfile destroy  or
  change the default protocol subfile we established in the first
  two commands?

12.11  (Using protocols in the PUBLIC PROTOCOLS subfile)

PART A:
  First let's use SPIRES  commands  to  see  what  protocols  are
  stored  in  the  PUBLIC  PROTOCOLS  subfile.   Each protocol is
  stored as a separate record, with  the  name  of  the  protocol
  serving  as  the key of the record.  To determine the number of
  protocols in the subfile and their names, logon to the  system,
  call SPIRES, and type in the following:

    -? SELECT PUBLIC PROTOCOLS
    -? SHOW FILE SIZE
    -? FOR GOAL
    +? SHOW KEYS ALL
    +? END
Q1:
  How many protocols (records) are there in the PUBLIC  PROTOCOLS
  subfile?
Q2:
  How many "keys" or protocol names were listed by the SHOW  KEYS
  ALL command?
PART B:
  Using a protocol from the PUBLIC PROTOCOLS file is very simple.
  Let's try the COMPUTER protocol  as  an  example.   This  is  a
  protocol  evaluates  any  numeric  expression  you type in.  So
  let's use it to evaluate a simple expression like 48/3+2.  Type
  in the following to  invoke  the  COMPUTER  protocol  and  when
  prompted  for  an  expression  enter  48/3+2.  To terminate the
  protocol, press  the  attention  key  instead  of  entering  an
  expression.

    -? SELECT PUBLIC PROTOCOLS
    -? SET XEQ
    -? XEQ FROM COMPUTER

  Now try the ".." form of  the  XEQ  command.   Since  you  have
  already  established  PUBLIC  PROTOCOLS as the default protocol
  subfile, you need only type in:

    -? ..COMPUTER
Q1:
  Did the execution of  the  protocol  change  in  any  way  when
  invoked by the ".."  form of the XEQ command?
Q2:
  Can you use the ".." form of  the  XEQ  command  to  execute  a
  protocol  stored  in the active file?  If you are not sure, try
  it or review section 3.2.
PART C:
  Now let's try executing the COMPUTER protocol with a  parameter
  which indicates that the instructions normally listed are to be
  bypassed.   The parameter follows the ..COMPUTER command.  This
  protocol has been written  so  that  it  treats  any  parameter
  included  in  the  ".."  command  as instructions to bypass the
  explanation.   Since  the  characters  in  the  parameter   are
  unimportant,  let's  use  the word "ANYTHING" as our parameter.
  Type in the following to see how this works:

  -?  ..COMPUTER ANYTHING
Q1:
  Did you get any instructions?
Q2:
  Can you use this parameter option  on  all  forms  of  the  XEQ
  command  or  only  the ".."  form?  If you are not sure re-read
  section 3.2.

12.12  (Listing protocols in the PUBLIC PROTOCOLS subfile)

PART A:
  You may be interested in looking at the commands which make  up
  a  protocol stored in the PUBLIC PROTOCOLS subfile.  One way to
  do this is to use the normal SPIRES commands  for  selecting  a
  subfile and displaying a record in that subfile.  While most of
  the  protocols  in  this  subfile  use features of the protocol
  language which you may  not  have  studied  yet,  many  of  the
  commands  in  the COMPUTER protocol, used in the last exercise,
  should be familiar.  Logon to the system, call SPIRES, and type
  in the following to get a  display  of  the  commands  in  this
  protocol:

    -? SELECT PUBLIC PROTOCOLS
    -? DISPLAY COMPUTER

  The "!" prefix which you see at the beginning of several of the
  lines simply inhibits the listing of that  command,  regardless
  of  the  current  ECHO  setting.   You  should  now  be able to
  understand all other lines in the protocol except  the  second,
  third, and fifteenth lines which use some advanced features.
PART B:
  If you would like to have a display of the protocol with WYLBUR
  line numbers as well as a copy of the protocol  in  the  active
  file, type in the following:

    -? IN ACTIVE CLEAR DISPLAY COMPUTER
Q1:
  Study the IF command at line 5.  What  system  variable  is  it
  testing?   Are  there  any  ASK  commands ahead of line 5 which
  could have assigned a user response to $ASK?
Q2:
  Based on your answers to question  1  and  your  experience  in
  doing  part  C  of  exercise  11,  can  you guess what value is
  currently assigned to $ASK at line 5?  If you are stumped, look
  at the JUMP statement in the command.
Q3:
  Examine the ASK command at line 10.  What should happen if  you
  execute the protocol and only type in a carriage return as your
  response?
PART C:
  Any protocol  record  in  the  PUBLIC  PROTOCOLS  file  may  be
  displayed  in one of several formats.  The format which you saw
  in part B of this exercise has been named the "PUBLIC"  format.
  If  you  would like to see the other formats available, type in
  the following:

    -? SHOW FORMATS

  The phrase "(SELECTED)" always appears in the  system  response
  to the right of the format name which is currently the default.

  In addition to these formats, there is a default system format.
  To see  the  system  default  format,  type  in  the  following
  commands:

    -? CLEAR FORMATS
    -? SHOW FORMATS
    -? IN ACTIVE CLEAR DISPLAY COMPUTER

  Notice that the first line now begins with NAME=  and  all  the
  rest  of  the lines now begin with COMMAND=.  Furthermore, each
  line ends in a semicolon.  This reflects the structure shown in
  section 3.2 for every record in any protocol subfile.

  When a SPIRES file is created, it is up to the  file  owner  to
  decide  whether  or not he wishes to ADD, TRANSFER, UPDATE, and
  DISPLAY records in a form other than the system default.  While
  SPIRES does not require that there be  special  formats  for  a
  subfile,  more often than not users wish to add and/or view the
  records in their subfiles in a format  other  than  the  system
  default.   Additionally,  file owners often develop formats for
  generating reports.
Q1:
  Are the commands displayed between "COMMAND=" and ";" under the
  default  system  format  the  same  as those displayed when the
  PUBLIC format was in effect?
Q2:
  Can you now see why many  people  choose  to  define  alternate
  formats  for  adding, updating, and displaying records in their
  subfiles?

12.13  (Establishing your own protocol subfile)

PART A:
  If you plan to develop more than 2 or  3  protocols,  you  will
  want  to  establish your own protocol subfile for storing them.
  If you have never done a SPIRES file definition, or even if you
  have, you can use  the  PERFORM BUILD PROTOCOLS command.   This
  utility  not  only  establishes  the file definition needed for
  your  protocol  subfile,  but   also  establishes   a    format
  definition  for  your  subfile.  This  format will display your
  protocol   records   without  the  NAME=,  COMMAND=,  and   ";"
  elements.   It will also automatically add the NAME=,  COMMAND=
  and  ";"  elements  to  any  protocol you have developed in the
  active file and are adding to your subfile.  This will save you
  a great  deal  of  time  and   effort   in   maintaining   your
  protocols.

  When you execute the PERFORM BUILD PROTOCOLS command, you  will
  be asked for two things:

    .  the name you wish to use for your protocol subfile
    .  a list of accounts, other than your own, that may update or
       add records to your subfile

  The utility then builds a file definition for your subfile  and
  adds  it  to the system  FILEDEF  file, and compile it for you.
  To use this utility, simply call SPIRES and issue the command

    -? perform build protocols

  For  more  details  about  this  utility,  see  Chapter  28  in
  "Technical Notes" or EXPLAIN PERFORM BUILD PROTOCOLS.

12.14  (Adding protocols to your subfile and executing them)

PART A:

Let's create a protocol in the active file and check that it executes properly. Once checked out you will select your protocol subfile and ADD the protocol to it. For the purposes of this example, the name of your protocols subfile is shown as "your-protocol-subfile-name." The protocol to be added will be called PROTONAMES.

The PROTONAMES protocol, when executed, will list out the names of all protocols currently stored in your protocol subfile or a message stating that there are no protocol records in your protocol subfile. The names of protocols which have been removed today will still appear, and those which are added today will not appear.

The first line of the protocol should be a "*" command containing the name of the protocol. While checking out the protocol in the active file, this will cause the protocol name to print at the terminal. However when the protocol is added to your protocol subfile, this first line will be used by the PUBLIC format to create the "NAME=" field of the record. Logon to the system, call SPIRES, and type in the following:

    -> COLLECT CLEAR
        1.   ? * PROTONAMES
        2.   ? !SET NOECHO
        3.   ? -  lists the names of protocols
        4.   ? -  stored in my protocol subfile or deferred queue.
        5.   ? SET MES=0
        6.   ? LET OLDFILE=" "
        7.   ? IF $SELECTED THEN LET OLDFILE=$SELECT
        8.   ? SELECT your-protocol-subfile-name
        9.   ? *
       10.   ? *PROTOCOL NAME LIST
       11.   ? *------------------
       12.   ? FOR GOAL
       13.   ? SHOW KEYS ALL    END="JUMP NORECS"
       14.   ? END
       15.   ? ++ENDING
       16.   ? IF #OLDFILE~= " " THEN / SELECT #OLDFILE
       17.   ? SET MES=2
       18.   ? RETURN
       19.   ? ++NORECS
       20.   ? *NO PROTOCOLS CURRENTLY IN SUBFILE
       21.   ? JUMP ENDING
       22.   ? (attn)
    ->

Be sure you put the name of your protocol subfile in line 8. Now execute the protocol from the active file to be sure it works.

Q1:
  Which of the three messages did you get when you  executed  the
  protocol?
Q2:
  If  you  selected  another  file  such  as  FILEDEF  and   then
  re-executed  the  protocol,  would the FILEDEF subfile still be
  selected after the protocol executed?  If you are not sure, try
  typing in the following:

    -> SELECT FILEDEF
    -> /*  $SELECTED  $SELECT
    -> XEQ
       .
       .
       .
    -> /*  $SELECTED  $SELECT
PART B:

Now let's add the protocol to your protocol subfile. Again, for the purposes of this example we will use the term "your-protocol-subfile-name" wherever you should substitute the name of your protocol subfile name.

Remember that when PERFORM BUILD PROTOCOLS created your protocols subfile, it also generated a format called $PROTOCOL, automatically used whenever your subfile is selected. When you are adding or updating a protocol record, one of the purposes of the $PROTOCOL format is to add NAME=, the COMMAND=, and the terminating ";" for each line to the commands in the active file so that it conforms to the definition of a protocol record. This formatting occurs automatically when you have selected your protocol subfile and then say ADD or UPDATE.

The only data other than the protocol commands in the active file which you must provide is a * command as the first line of the active file with a name which designates the name for the protocol record. List the protocol you now have in the active file and make sure that the first line meets this requirement, then select your protocol subfile and add the record as follows:

    -> LIST FIRST
    -> SELECT your-protocol-subfile-name
    -> ADD
PART C:

Now let's try executing the new protocol from the protocol subfile. Clear the active file and try the "..protocol-name" command to execute the protocol by typing in the following:

    -> CLEAR ACTIVE
    -> SELECT your-protocol-subfile-name
    -> SET XEQ
    -> ..PROTONAMES
Q1:
  Did you get a different response  than  in  Part  A?   Can  you
  explain why?

12.15  (Displaying and modifying protocols in your protocol subfile)

PART A:
  Whenever you wish to examine the statements in a protocol,  you
  can  display  the  commands in a protocol at the terminal using
  the DISPLAY command.

    -? SELECT your-protocol-subfile-name
    -? DISPLAY PROTONAMES

  Notice  that  the  PUBLIC  format  has  stripped  off  NAME  =,
  COMMAND=, and ";" terminators before  displaying  each  command
  and placing it in the active file.
PART B:
  Now let's modify PROTONAMES slightly to obtain both a  list  of
  protocols  which  have  been in the protocol subfile for one or
  more days and a list of those which have been added or  updated
  today.

  We will first change the FOR GOAL command at line 12  to  be  a
  FOR TREE command so that we will recieve only the keys or names
  of protocols which have been in the protocol subfile for one or
  more days.  We will then insert a new set of commands that will
  examine  only the records in the deferred queue of the subfile.
  We will also change the various messages  in  the  protocol  to
  reflect the new situation.

  First select your protocol subfile and transfer  PROTONAMES  to
  the  active  file.   Modify the protocol and finally, issue the
  UPDATE command to replace the protocol in the protocol subfile.
  The PUBLIC format will strip NAME=, COMMAND= and ";"  from  the
  protocol  record  when  you  TRANSFER.   Similiarly, the PUBLIC
  format will reinsert these  items  after  you  have  made  your
  modifications and issued the UPDATE command.

    -? SELECT your-protocol-subfile-name
    -? TRANSFER PROTONAMES
    -? REPLACE 10 LIST
       10.   ? *PROTOCOL NAME LIST
       11.   ? *OLD PROTOCOL NAMES
    -? REPLACE 12 LIST
       12.   ? FOR GOAL
       12.   ? FOR TREE
    -? COLLECT 14.1 LIST
       14.1  ? ++DEFQS
       14.2  ? *
       14.3  ? *PROTOCOLS IN DEFQ
       14.4  ? *---------------------------
       14.5  ? FOR DEFQ
       14.6  ? SHOW KEYS ALL   END="JUMP NODEFQS"
       14.7  ? END
       14.8  ? (attn)
    -? REPLACE 20 LIST
       20.   ? *NO PROTOCOLS CURRENTLY IN SUBFILE
       20.   ? *NO OLD PROTOCOLS IN SUBFILE
    -? REPLACE 21 LIST
       21    ? JUMP ENDING
       21.   ? JUMP DEFQS
    -? COLLECT END
       22.   ? ++NODEFQS
       23.   ? *NO PROTOCOL MODIFICATIONS OR ADDITIONS IN DEFQ
       24.   ? JUMP ENDING
       25.   ? (attn)
    -? NUMBER
    -? LIST
         1.   ? * PROTONAMES
         2.   ? !SET NOECHO
         3.   ? -  lists the names of protocols
        4.   ? -  stored in my protocol subfile or deferred queue
        5.   ? SET MES=0
        6.   ? LET OLDFILE=" "
        7.   ? /IF $SELECTED THEN LET OLDFILE=$SELECT
        8.   ? SELECT your-protocol-subfile-name
        9.   ? *
       10.   ? *OLD PROTOCOL NAMES
       11.   ? *------------------
       12.   ? FOR TREE
       13.   ? SHOW KEYS ALL    END="JUMP NORECS"
       14.   ? END
       15.   ? ++DEFQS
       16.   ? *
       17.   ? *PROTOCOLS IN DEFQ
       18.   ? *-----------------
       19.   ? FOR DEFQ
       20.   ? SHOW KEYS ALL    END="JUMP NODEFQS"
       21.   ? END
       22.   ? ++ENDING
       23.   ? /IF #OLDFILE~= " " THEN SELECT #OLDFILE
       24.   ? SET MES=2
       25.   ? RETURN
       26.   ? ++NORECS
       27.   ? *NO OLD PROTOCOLS IN SUBFILE
       28.   ? JUMP DEFQS
       29.   ? ++NODEFQS
       30.   ? *NO PROTOCOL ADDS OR MODIFICATIONS IN DEFQ
       31.   ? JUMP ENDING
    -? XEQ
Q1:
  Which message(s) did you get when you  executed  the  protocol?
  Can you explain why you got that particular message rather than
  one of the others?

12.16  (Establishing a SPIRES entry protocol)

PART A:
  If you are using  protocols  frequently  when  you  work  under
  SPIRES  and also have several SPIRES session defaults which you
  establish each time you call SPIRES, you may wish to  establish
  an  "entry"  protocol which is automatically executed each time
  you call SPIRES. This entry  protocol  is  not stored  in  your
  private  protocols  subfile, but in the system protocol subfile
  called ENTRY COMMANDS.

  To add  an  entry  protocol  to  the  ENTRY  COMMANDS  protocol
  subfile,  you  simply  collect  in the active file the protocol
  commands you desire  to  have  executed  each  time  you  enter
  SPIRES,  then  SELECT  the ENTRY COMMANDS subfile, and ADD your
  record.  The first command in the active file  when  you  issue
  the  ADD  command must be a "*" command containing your account
  number in the form "gg.uuu".  This will be used as the name (or
  "key") of your protocol record in the ENTRY COMMANDS subfile so
  that you can display and modify it at some future time.

  First write down the sequence of commands which you would  like
  to  have executed each time you call SPIRES.  A possible set of
  such commands are shown below for the purpose  of  an  example,
  but  you  should  devise  your own set of commands.  Notice the
  account number given in the first line; substitute your own  in
  the form gg.uuu.

  EXAMPLE :

    -? SELECT ENTRY COMMANDS
    -? COLLECT CLEAR
        1.   ? * GG.LCL
        2.   ? !SET NOECHO
        3.   ? SET LENGTH 80
        4.   ? SET UPPER
        5.   ? SET VOLUME PUB002
        6.   ? SELECT MYPROCS
        7.   ? SET XEQ
        8.   ? ..PROTONAMES
        9.   ? RETURN
       10.   ? (attn)
    -? ADD

  Notice that we are invoking a protocol from within  a  protocol
  by the use of the ..PROTONAMES command.

:  Appendices

:1  Error Messages for VGROUP Compilation

The following are error messages that you may receive when you try to compile a vgroup definition.

:4.2.1.6.1  * LONG VARIABLE HAS OCC > 1

LONG VARIABLE HAS OCC > 1

A variable with a LEN value greater than 253 also has an OCC value greater than one. A variable of that length must be singly occurring.

:4.2.1.6.2  * BAD LEN FOR VARIABLE TYPE

BAD LEN FOR VARIABLE TYPE

A LEN value coded for a variable is invalid for the given variable type.

:4.2.1.6.3  * VARIABLE SUBSCRIPTING PROBLEM

VARIABLE SUBSCRIPTING PROBLEM

A static variable that was declared singly occurring in its definition has been used with a subscript indicating a multiple occurrence, causing this compilation error.

:4.2.1.6.4  * UNKNOWN VARIABLE TYPE

UNKNOWN VARIABLE TYPE

An invalid type was specified for a static variable.

:4.2.1.6.5  * VGROUP TOO LARGE

VGROUP TOO LARGE -- TRY DYNAMIC TYPE

The total length to be allocated for a variable group is larger than the system maximum. Try defining vgroup arrays with TYPE=DYNAMIC, or splitting the vgroup into two or more vgroups.

:4.2.1.6.6  * VGROUP VALUE CONVERSION ERROR

VGROUP VALUE CONVERSION ERROR

The data in the VALUE statement for this variable could not be converted properly for preloading into the variable.

:4.2.1.6.7  * VGROUP VALUE ERROR

VGROUP VALUE -- TOO LONG OR TOO MANY

The value to be preloaded into the current variable, specified in the VALUE statement, was either too long or had more occurrences than the OCC value for the variable allows.

:4.2.1.6.8  * VALUE INCONSISTENT WITH MULTIPLE OCCS

VALUE INCONSISTENT WITH MULTIPLE OCCS

An attribute being compiled for this variable has a different number of occurrences than are allowed by the OCC value. For instance, if "OCC = 3,4;", indicating a two-dimensional array, then INDEXED-BY must have two values. If it had one or three values, an error would occur.

:4.2.1.6.9  * VALUE MUST BE INTEGER

VALUE MUST BE A 2 OR 4 BYTE INTEGER

The variable name specified on INDEXED-BY must reference a two-byte or four-byte integer variable. For example, if "INDEXED-BY = variablename" then "variablename" must be a two-byte or four-byte integer.

:4.2.1.6.10  * MULTIPLE OCC VALUE MUST BE INDEXED

MULTIPLE OCC VALUE MUST BE INDEXED

If the OCC statement has more than a single value, indicating a two- or three-dimensional array, then the INDEXED-BY statement must be coded.

:4.2.1.6.11  * INCONSISTENT STRING REDEFINITION

INCONSISTENT STRING REDEFINITION

A non-string variable may not redefine a string variable, nor can a string variable redefine a non-string variable.

:4.2.1.6.12  * REDEFINITION LOOPING PROBLEM

REDEFINITION LOOPING PROBLEM

A variable being redefined is itself redefined by another variable. This is acceptable unless the recursive redefinition of variables is greater than five or unless the redefinitions somehow lead back to the original variable. For example, if a variable being redefined is redefining the variable that redefines it, that is not allowed.

:4.2.1.6.13  * ILLEGAL USAGE WITH DYNAMIC TYPE

ILLEGAL USAGE WITH DYNAMIC TYPE

INDEXED-BY or redefinition may not be used by variables of type DYNAMIC.

:4.2.1.6.14  * VARIABLE TABLE OVERFLOW

VARIABLE TABLE OVERFLOW

An internal table used to process variables has been filled. The vgroup is too large.

:4.2.1.6.15  * REDEFINED VARIABLE CANNOT HAVE VALUE

REDEFINED VARIABLE CANNOT HAVE VALUE

Variables that redefine other variables may not have pre-assigned values. The variable being redefined may have assigned values.

:29  SPIRES Documentation

I. Primers

II. User Language

III. Application Development

IV. Reference Guides (Cards)

V. Prism

VI. SPIRES Aids and Study Materials

VII. Other Related Documents

(The following documents are not SPIRES documents per se, but describe utilities and programs that may be useful in developing SPIRES applications.)

Obtaining Documentation

The above documents (except any marked "in preparation") may be obtained through the PUBLISH command on the Forsythe computer at Stanford University. If you do not use SPIRES at Stanford, contact your local system administrator to find out how SPIRES documents are made available there.

Updates to SPIRES Manuals

SPIRES manuals are updated regularly as changes are made to the system. This does not mean that all manuals are out of date with each new version of SPIRES. The changes to the documentation match those made to SPIRES: they are usually minor and/or transparent. Not having the most current version of a manual may mean you do not have all the most recent information about all the latest features, but the information you do have will usually be accurate.

A public subfile, SPIRES DOC NOTES, contains information about changes to SPIRES manuals. Using this subfile, you can determine whether the manual you have has been updated and if so, how significant those updates are. You need to know the date your manual was published, which is printed at the top of each page. For details on the procedure, issue the command SHOW SUBFILE DESCRIPTION SPIRES DOC NOTES.


INDEX


++ PREFIX   1.7
++ PREFIX FOR LABEL STATEMENTS   3.2
|| OPERATOR FOR CONCATENATION   5.1.1.1
! PREFIX   5.7
$ABS FUNCTION   7.2.0b
$ACCESSTEST FUNCTION   7.2.0c
$ACCOUNT VARIABLE   6.4.2
$ACTNO VARIABLE   6.4.3
$ACTNUM VARIABLE   6.4.3
$AMATCH FUNCTION   7.2.1
$ANY VARIABLE   7a.3
$APMATCH FUNCTION   7.2.2
$AREANAME VARIABLE   6.4.5
$ARMATCH FUNCTION   7.2.2a
$ASCII FUNCTION   7.2.2b
$ASET FUNCTION   7.2.3
                 4.2.2
$ASK VARIABLE   2.2
                6.4.4
                5.11
                5.3
                5.4
$ASORT FUNCTION   7.2.3a
$ASORTX FUNCTION   7.2.3a
$ATTRIBUTE FUNCTION   7a.8
$AUTHPARMS FUNCTION   7.2.3b
$BIN VARIABLE   6.4.6
$BLDLCTR FUNCTION   7.2.3.B
$BNF FUNCTION   7.2.3c
$BNF SEMANTICS   7.2.3.D
$BREAK FUNCTION   7.2.4
$CAP FUNCTION   7.2.4a
$CAPITALIZE FUNCTION   7.2.4a
$CASE FUNCTION   7.2.5
$CHANGE FUNCTION   7.2.6
$CHANGELIST FUNCTION   7.2.6a
$CHAR FUNCTION   7.2.7
$CHARACTER FUNCTION   7.2.7
$CHECK FUNCTION   7.2.7a
$CHRI VARIABLE   6.4.8
                 6.1
$CHRX VARIABLE   6.4.8
                 6.1
$CLEAR VARIABLE   6.5.0
                  6.2.1
                  6.1
$CLR VARIABLE   6.5.0
$CLRSUBF FUNCTION   7.2.7b
$COLUMNTEST FUNCTION   7.2.7c
$COMMAND VARIABLE   6.5.2
$COMPARE FUNCTION   7.2.8
$COMPXEQ VARIABLE   10.3
                    6.5.4
$CPUTIME VARIABLE   6.5.6
$CRTMODE VARIABLE   6.5.7
$CURCMD VARIABLE   6.5.8
$DATE VARIABLE   6.5.9
$DATECC VARIABLE   6.5.9
$DATEIN FUNCTION   7.2.9
$DATEOUT FUNCTION   7.2.9.1
$DATEOUT FUNCTION FOR DATETIME   7.2.9.4
$DATETIME FUNCTION   7.2.9.3
$DAYS FUNCTION   7.2.9.2
$DAYS FUNCTION, USED WITH $XDATE   7.9.3b
$DECIMAL FUNCTION   7.3.1
$DECODE FUNCTION   7.3.8b
$DEFQLOAD VARIABLE   6.6.0
                     6.2.1
                     6.1
$DEFQTEST FUNCTION   7.3.2
$DELTA VARIABLE   6.6.2
$DENCODE FUNCTION   7.3.2a
$DIAG VARIABLE   6.6.4
                 6.2.1
                 6.1
$DISCHR VARIABLE   6.16.0
$DOUBLE FUNCTION   7.3.3
$DYNASET FUNCTION   7.3.4
$DYNAZAP FUNCTION   7.3.4a
$DYNGET FUNCTION   7.3.5
$DYNPUT FUNCTION   7.3.5
$DYNZAP FUNCTION   7.3.5
$ECHO VARIABLE   6.6.8
                 6.2.1
                 6.1
$EDIT FUNCTION   7.3.6
$EDITOR VARIABLE   6.6.9
$ELEMINFO FUNCTION   7.3.7
$ELEMTEST FUNCTION   7.3.8
$ELIDINFO FUNCTION   7.3.7
$ELIDTEST FUNCTION   7.3.8
$ELNOTEST FUNCTION   7.3.8
$ELOGSCNT VARIABLE   6.6.9b
                     8.4.3
$ELOGWCNT VARIABLE   6.6.9b
                     8.4.3
$ELSE VARIABLE   3.1.3
                 6.7.0
                 5.8.1
$ENCIPHER FUNCTION   7.3.8a
$ENCODE FUNCTION   7.3.8b
$ENUM VARIABLE   6.7.2
$ENVIRON VARIABLE   6.7.4
$ERRCODE VARIABLE   6.7.6
$ETIME VARIABLE   6.7.7
$EVAL FUNCTION   7.3.9
$EVALUATE FUNCTION   7.3.9
$EXP FUNCTION   7.4.1
$FACTORIAL FUNCTION   7.4.1b
$FACTORS FUNCTION   7.4.1c
$FALSE VARIABLE   6.20.6
$FILEDATE VARIABLE   6.7.8
$FILENAME VARIABLE   6.8.0
$FILETIME VARIABLE   6.7.8
$FINDSTAT VARIABLE   6.8.2
$FLAG FUNCTION   7.4.2
$FORMAT VARIABLE   6.8.4
$FORTYPE VARIABLE, IN PROTOCOLS   6.8.6
$FRAME VARIABLE, IN PROTOCOLS   6.8.8
$FRAMETEST FUNCTION   7.4.2a
$GCODE VARIABLE   6.21.4
$GETCVAL FUNCTION   7.4.3c
                    7.4.3
$GETCVAL FUNCTION, AND THE $GETXPATH/$GETSPATH VARIABLES   7.4.3a
$GETELOG FUNCTION   8.4.3
                    7.4.2b
$GETIVAL FUNCTION   7.4.3c
                    7.4.3
$GETIVAL FUNCTION, AND THE $GETXPATH/$GETSPATH VARIABLES   7.4.3a
$GETPARMS FUNCTION   7.4.2c
                     6.13.1b
                     4.5.2
                     4.5.1
$GETSPATH VARIABLE   7.4.3c
                     7.4.3b
                     6.9.0
                     7.5.4a
$GETSPATH VARIABLE, AND THE $GETCVAL/$GETUVAL/$GETIVAL/$GETXVAL FUNCTIONS   7.4.3a
$GETUVAL FUNCTION   7.4.3c
                    7.4.3
$GETUVAL FUNCTION, AND THE $GETXPATH/$GETSPATH VARIABLES   7.4.3a
$GETVMATCH FUNCTION   7.4.3b
$GETVOCC FUNCTION   7.4.3c
$GETXPATH VARIABLE   7.4.3c
                     7.4.3b
                     6.9.0
                     7.5.4a
$GETXPATH VARIABLE, AND THE $GETCVAL/$GETUVAL/$GETIVAL/$GETXVAL FUNCTIONS   7.4.3a
$GETXVAL FUNCTION   7.4.3c
                    7.4.3
$GETXVAL FUNCTION, AND THE $GETXPATH/$GETSPATH VARIABLES   7.4.3a
$GLOFORMAT VARIABLE   6.9.2
$GOALREC VARIABLE   6.9.4
$GPCOUNT VARIABLE   6.9.6
$GRO VARIABLE   6.21.4
$GROUP FUNCTION   7a.9
$GROUPELEMENT FUNCTION   7a.12
$GROUPSIZE FUNCTION   7a.10
$GROUPSORT FUNCTION   7a.11
$GRPNULL VARIABLE   7a.9
$GXCOUNT VARIABLE   6.9.6
$HEX FUNCTION   7.4.4
$IDATA VARIABLE   6.9.7
$INDEXINFO FUNCTION   7.4.5
$INDEXNUM FUNCTION   7.4.5.1
$INDEXTERM FUNCTION   7.4.5.2
$INFOI VARIABLE   6.9.8
                  6.1
$INFOX VARIABLE   6.9.8
                  6.1
$INSERT FUNCTION   7.4.5a
$INSETC FUNCTION   7.4.6
$INSETL FUNCTION   7.4.6
$INSETR FUNCTION   7.4.6
$INT FUNCTION   7.4.7
$INTEGER FUNCTION   7.4.7
$INTXEQ VARIABLE   6.9.9
$IOCOUNT VARIABLE   6.10.0
$ISSUECMD FUNCTION   7.4.8
$ISSUEMSG FUNCTION   7.4.8a
$JOBNUM VARIABLE   6.10.1
$KEY VARIABLE   6.10.2
$LASTRESNUM VARIABLE   6.10.3
$LASTXEQ VARIABLE   6.10.4
                    2.4
$LEFTSTR FUNCTION   7.4.9
$LEFTSUB FUNCTION   7.5.1
$LENGTH VARIABLE   6.10.6
$LINE FUNCTION   7.5.2
$LIST VARIABLE   6.10.8
                 6.2.1
                 6.1
$LOG FUNCTION   7.5.3
$LOOKSUBF FUNCTION   7.5.4
$LOOKSUBG FUNCTION   7.5.4
$LOOKSYS FUNCTION   7.5.4a
$LOOKUP FUNCTION   7a.7
$LSTR FUNCTION   7.4.9
$LSTRIP FUNCTION   7.7.3c
$LSUB FUNCTION   7.5.1
$MADE FUNCTION   7a.4
$MAIL VARIABLE   6.11.0
$MAKE FUNCTION   7a.1
$MATCH FUNCTION   7.5.5
$MCODE VARIABLE   6.7.6
$MINCHR VARIABLE   6.16.0
$MLEV VARIABLE   6.11.4
$MNUM VARIABLE   6.11.8
$MOD FUNCTION   7.5.6
$MSGCODE VARIABLE   6.7.6
$MSGINT VARIABLE   7.8.3a
                   6.11.2
$MSGLEV VARIABLE   6.11.4
$MSGLIT VARIABLE   6.11.6
$MSGNUM VARIABLE   6.11.8
$MULTILOG VARIABLE   6.12.0
$NAME VARIABLE   6.12.2
$NEG FUNCTION   7.5.7
$NEGATIVE FUNCTION   7.5.7
$NEW VARIABLE   7a.2
$NEXTSLOT VARIABLE   6.12.4
$NEXTSLOTKEY VARIABLE   6.12.4
$NEXTWDSR VARIABLE   6.12.6
                     5.11
$NEXTWDSW VARIABLE   6.12.8
$NO VARIABLE   6.24.2
               5.8.1
$NOCLEAR VARIABLE   6.5.0
                    6.2.1
                    6.1
$NOCLR VARIABLE   6.5.0
$NODIAG VARIABLE   6.6.4
                   6.2.1
                   6.1
$NOECHO VARIABLE   6.6.8
                   6.2.1
                   6.1
$NOLEV VARIABLE   6.13.0
$NOLF FUNCTION   7.5.8
$NOLIST VARIABLE   6.10.8
                   6.2.1
                   6.1
$NORMALIZE FUNCTION   7.5.8b
$NOSTOP VARIABLE   6.17.6
                   6.2.1
                   6.1
$NOT FUNCTION   7.4.2
$NOTRACE VARIABLE   6.20.2
                    6.2.1
                    6.1
$NOTSLOT VARIABLE   6.17.2
$NZRESULT VARIABLE   6.24.4
$OBJECT FUNCTION   7a.8
$ODATA VARIABLE   6.13.1
$PACK FUNCTION   7.5.9
$PACKED FUNCTION   7.5.9
$PACKTEST FUNCTION   7.6.1
$PARM VARIABLE   6.13.1a
$PARMCNT VARIABLE   6.13.1b
$PARSE FUNCTION   7.6.2
$PARSESTRIP FUNCTION   7.6.2
$PATHCUR VARIABLE   6.13.2
$PATHFIND FUNCTION   7.6.2a
$PATHINFO FUNCTION   7.6.2a
$PATHKEY VARIABLE REFERENCE   6.13.3
$PATHNUM VARIABLE REFERENCE   6.13.4
$PMATCH FUNCTION   7.6.3
$PPCOUNT VARIABLE   6.9.6
$PRECISION FUNCTION   7.6.4
$PRECMD VARIABLE   6.5.8
$PREMAC VARIABLE   6.5.8
$PRISMINFO FUNCTION   7.6.4a
$PROCSUBG FUNCTION   7.6.5
$PROGRAM VARIABLE   6.13.6
$PROMPT VARIABLE   6.13.8
                   5.3
$PROTOCOL FORMAT   9.1
$PROXYID VARIABLE   6.13.9
$PRTCNT VARIABLE   6.14.0
$PRTLVL VARIABLE   6.14.2
$PSNUM VARIABLE   6.7.2
$PXCOUNT VARIABLE   6.9.6
$RANDOM FUNCTION   7.6.6
$RC VARIABLE   6.15.6
$REAL FUNCTION   7.6.7
$RECINFO FUNCTION   7.6.7a
$RECSEP VARIABLE   6.14.6
$RECTEST FUNCTION   7.6.8
$REF FUNCTION   7.6.8a
                4.5.2
$RELPOS VARIABLE   6.14.8
$REMAINDER FUNCTION   7.6.9
$RESCNT VARIABLE   6.15.0
$RESHIST VARIABLE   6.15.1
$RESINFO FUNCTION   7.7.0
$RESNAME VARIABLE   6.15.2
$RESNUM VARIABLE   6.15.3
$RESULT VARIABLE   6.15.4
$RETCODE VARIABLE   6.15.6
$RETYPE FUNCTION   7.7.1
                   4.1.1
$REVERSE FUNCTION   7.7.1b
$RIGHTSTR FUNCTION   7.7.2
$RIGHTSUB FUNCTION   7.7.3
$RMATCH FUNCTION   7.7.3a
$ROOT FUNCTION   7.7.3b
$RSTR FUNCTION   7.7.2
$RSTRIP FUNCTION   7.7.3c
$RSUB FUNCTION   7.7.3
$SCRNAREA VARIABLE   6.4.5
$SEARCHINFO FUNCTION   7.7.4
$SEARCHMOD VARIABLE   6.15.7
$SEARCHTEST FUNCTION   7.7.4a
$SEED VARIABLE   7.6.6
                 6.15.8
$SELCHR VARIABLE   6.16.0
$SELECT VARIABLE   6.16.2
$SELECTED VARIABLE   6.16.4
$SEP VARIABLE   5.7a
$SEP VARIABLE DEFINITION   6.16.6
$SERI VARIABLE   6.16.8
                 6.1
$SERX VARIABLE   6.16.8
                 6.1
$SET FUNCTION   7.7.4b
$SETFORMAT VARIABLE EXAMPLE   6.17.0
$SETPARMS FUNCTION   7.7.4c
                     6.13.1b
                     4.5.2
                     4.5.1
$SITE VARIABLE   6.17.1
$SIZE FUNCTION   7.7.5
$SLOT VARIABLE   6.17.2
$SNUM VARIABLE   6.7.2
$SORTCODE VARIABLE EXAMPLE   6.17.3
$SPAN FUNCTION   7.7.6
$SQRT FUNCTION   7.7.7
$SQU FUNCTION   7.7.8
$SRCHMOD VARIABLE   6.15.7
$SRCHSTAT VARIABLE   6.8.2
$SSW FUNCTION   7.7.9
$STACK VARIABLE   6.17.4
$STATGET FUNCTION   7.8.0
$STATPUT FUNCTION   7.8.0
$STOP VARIABLE   6.17.6
                 6.2.1
                 6.1
$STR FUNCTION   7.8.1
$STRING FUNCTION   7.8.1
$STRIP FUNCTION   7.8.2
$SUBCODE VARIABLE REFERENCE   6.17.8
$SUBFSIZE VARIABLE   6.18.0
$SUBLATCH VARIABLE   6.18.2
$SUBSTR FUNCTION   7.8.3
$SUPERMAX VARIABLE   6.18.4
$SUPERVAL VARIABLE   6.18.4
$SYSEVAL FUNCTION   7.8.3a
$SYSINFO FUNCTION   7.8.3b
$SYSTEM FUNCTION   7.8.4
$SYSTYPE VARIABLE   6.18.5
$TCOMMENT VARIABLE REFERENCE   6.18.6
$TERMINAL VARIABLE   6.18.8
$TERMTYPE VARIABLE   6.19.2
$TEST FUNCTION   7.8.5
$TIME VARIABLE   6.19.4
$TIMEIN FUNCTION   7.8.6
$TIMELIMIT VARIABLE   6.19.6
                      5.20a
$TIMEOUT FUNCTION   6.7.7
                    7.8.6
$TIMER VARIABLE   6.19.8
$TRACE VARIABLE   6.20.2
                  6.2.1
                  6.1
$TRANINFO FUNCTION   7.8.6b
$TRANSFER VARIABLE   6.20.4
$TRANSLATE FUNCTION   7.8.7
$TRIM FUNCTION   7.2.7
$TRINULL VARIABLE   7a.7
$TRUE VARIABLE   6.20.6
$TRUNC FUNCTION   7.8.8
$TTYPE VARIABLE   6.19.2
$TYPE FUNCTION   7.8a
$TYPETEST FUNCTION   7.9.1
$UBLDLCTR FUNCTION   7.2.3.B
$UCD VARIABLE   6.21.6
$UCODE VARIABLE   6.21.6
$UDATE VARIABLE   6.5.9
$UNEDIT FUNCTION   7.9.1.1
$UNIVID VARIABLE   6.20.7
$UNMAKE FUNCTION   7a.5
$UNMAKETRIPLE FUNCTION   7a.6
$UPDTYPE VARIABLE   6.20.8
$UPLOW VARIABLE   6.21.2
                  6.2.1
                  6.1
$UPPER VARIABLE   6.21.2
                  6.2.1
                  6.1
$USER VARIABLE   6.21.4
$USERCODE VARIABLE   6.21.6
$USERNAME VARIABLE   6.21.8
$UTIME VARIABLE   6.19.4
$VALTER FUNCTION   7.9.1.3
$VALUE FUNCTION   7a.8
$VARGET FUNCTION   7.9.1.1a
$VARPUT FUNCTION   7.9.1.1a
$VARTEST FUNCTION   7.9.1.2
$VERIFY FUNCTION   7.9.1.2b
$VERSION VARIABLE   6.22.2
$VGROUPALTER FUNCTION   7.9.1.3
$VGROUPINIT FUNCTION   7.9.2
                       4.2.2
$VINIT FUNCTION   7.9.2
                  4.2.2
$WARNI VARIABLE   6.22.4
                  6.1
$WARNX VARIABLE   6.22.4
                  6.1
$WDS FUNCTION   7.9.3
$WDSL VARIABLE   6.22.6
$WDSLINE VARIABLE   6.22.6
$WDSR VARIABLE   6.22.8
                 5.11
$WDST VARIABLE   6.23.2
$WDSW VARIABLE   6.23.4
$WIDTH VARIABLE   6.23.6
$WINDOW FUNCTION   7.6.4
$WORKDAYS FUNCTION   7.9.3a
$XDATE FUNCTION   7.9.3b
$XEQ VARIABLE   6.23.8
$XEQLAST VARIABLE   6.10.4
$XEQLVL VARIABLE   3.2.2
                   6.23.9
$XEQSTACK FUNCTION   3.2.2
                     3.2.1
                     7.9.4
$XSTR FUNCTION   7.9.5
$XSUB FUNCTION   7.9.5a
$XTRACE VARIABLE   8.4.1
                   6.24.1
$YES VARIABLE   6.24.2
$YYCALC FUNCTION   7.9.5b
$YYTEST FUNCTION   7.9.5c
$ZAP FUNCTION   7.9.6
$ZRESULT VARIABLE   6.24.4
* COMMAND   5.14
- COMMAND   5.1
/ PREFIX   1.3
           5.17
           5.14
           5.7a
/ PREFIX, INCORRECT WITH THE LET COMMAND   5.1.1
/ PREFIX, WITH THE IF...THEN COMMAND   5.8
/ PREFIX, WITH THE SET COMMAND   5.2
// SEPARATOR CHARACTERS   5.7a
: PREFIX   5.7a
#   1.2
'..' COMMAND   2.2
'..' COMMAND, AND $ASK   6.4.4
ACCOUNT VALIDATION   7.8.4
ALLOCATE COMMAND   4.2.4
                   4.2.1
AREAS, NAME OF THE CURRENT AREA   6.4.5
ARRAY, DYNAMIC VARIABLE   7.2.2a
                          7.3.4a
                          7.3.4
                          7.2.2
                          7.2.1
ARRAY, STATIC VARIABLE   7.2.2a
                         7.2.3
                         7.2.2
                         7.2.1
ARRAYS, STATIC VARIABLE   4.3
ARRAYS, VARIABLE   4.3
ARRAYS, VARIABLES   4.1.1
ASK COMMAND   3.2.1
              5.3
              5.4
ASSIGNMENT, VARIABLE VALUE   5.1.1
ATTN STATEMENT ON THE ASK COMMAND   5.3
BACKSLASH CHARACTER, FOR CONTINUING PROTOCOL COMMANDS   5.1
BAD LEN FOR VARIABLE TYPE   :4.2.1.6.2
BEGIN BLOCK STATEMENT   3.1.1
BEGINBLOCK STATEMENT   3.1.1
BEGINBLOCK...ENDBLOCK   1.7
BLOCK CONSTRUCTS, AND THE $ELSE VARIABLE   3.1.3
BLOCK CONSTRUCTS, BEGINBLOCK   3.1.1
BLOCK CONSTRUCTS, INTRODUCTION   3.1
BLOCK CONSTRUCTS, NESTED   3.1.3
BLOCK CONSTRUCTS, OVERVIEW   1.7
BLOCK CONSTRUCTS, REPEAT...UNTIL   3.1.2
BLOCK CONSTRUCTS, RESTRICTIONS   3.1.4
BLOCK CONSTRUCTS, WHILE...ENDWHILE   3.1.2
BRANCHING   3.2.3
BREAK XEQ COMMAND   2.4
BUILDING A PROTOCOLS FILE   9.1
CATEGORIES OF FUNCTIONS   7.1
CHAINED BLOCK CONSTRUCTS   3.1.3
CHAINING PROTOCOLS   2.2.1
                     10.7
CHARACTER, FOR CONTINUING PROTOCOL COMMANDS   5.1
CHECK DIGITS   7.2.7a
CLEAR DYNAMIC VARIABLES COMMAND   4.4
                                  8.2
CLEAR DYNVAR COMMAND   4.4
CLEAR ELOG COMMAND   8.4.3
CLEAR ELOG DATA COMMAND   8.4.3
CLEAR MESSAGE NUMBER COMMAND   5.24
CLEAR TLOG COMMAND   8.4.2
CLEAR TLOG DATA COMMAND   8.4.2
CLEAR TRIPLES COMMAND   7a.13
CLEAR VGROUP COMMAND   4.2.1
CLEAR XEQ COMMAND   2.2.1
CLEAR XEQS COMMAND   2.2.1
                     2.4
CLEAR XTRACE COMMAND   8.4.1
COLON PREFIX   5.7a
COMMA PREFIX   5.8.2
COMMAND CHAINING   5.7a
COMMAND EXECUTION TRACING, GENERAL   8.4
COMMAND STATEMENTS   5.1
COMMAND, CLEAR TRIPLES   7a.13
COMMAND, SHOW TRIPLES   7a.13
COMMANDS, CONTINUING IN PROTOCOLS   5.1
COMMANDS, IN PROTOCOLS   5.1
COMMENT STATEMENTS   5.1
COMMENT STATEMENTS, AND BLOCK CONSTRUCTS   3.1.3
COMMENTS ON COMMANDS   5.1
COMPILE COMMAND, AND STATISTICS   4.1.2
                                  10.7
COMPILE COMMAND, FOR PROTOCOLS   10.2
                                 10.1
COMPILE NOT POSSIBLE -- NO USEMPROC VALUES   10.9
COMPILED PROTOCOLS   10.2.1
                     10.2
                     10.1
COMPILED PROTOCOLS, DETAILS OF COMPILING   10.2
COMPILED PROTOCOLS, EXECUTING   10.3
COMPILED PROTOCOLS, OLD METHOD TO NEW METHOD   10.9
COMPILED PROTOCOLS, OVERVIEW   10.1
COMPILED PROTOCOLS, STATEMENTS IN   10.4
COMPILED PROTOCOLS, STATISTICS   10.7
COMPILED PROTOCOLS, SYS PROTO   10.8
COMPILED PROTOCOLS, VARIABLE COMMUNICATION   10.5
COMPILED PROTOCOLS, WITH NON-COMPILED PROTOCOLS   10.6
COMPILED PROTOCOLS, ZAPPING   10.2.1
COMPILED VGROUPS   4.1.0
                   4.1.1
                   4.1
COMPILED VGROUPS, STATISTICS   4.1.2
COMPILING PROTOCOLS, OLD METHOD   10.8
COMPXEQ SUBFILE   10.3
CONCATENATION OPERATOR   5.1.1.1
CONDITION TESTING   1.6
                    5.8
                    3.1
CONDITION TESTING, AND BEGINBLOCK...ENDBLOCK   3.1.1
CONDITIONAL STATEMENTS WITH THE IF...THEN COMMAND   5.8
CONTINUE XEQ COMMAND   2.4
CONVERSION ERRORS   7.1.1
                    7.5.7
                    7.4.7
                    7.1
                    :4.2.1.6.6
                    5.1.2
CONVERT DATETIME TO HEX   7.2.9.3
DASH COMMAND   5.1
DATETIME CONVERSION TO HEX   7.2.9.3
DEALLOCATE COMMAND   4.2.4
                     4.2.1
DEBUGGING A PROTOCOL   8
                       2.4
DECLARE GLOBAL VGROUPS COMMAND BLOCK   4.2.1
DECLARE VGROUP COMMAND   4.1.0
DEFINE ELEMENT COMMAND   7.9.6
DIAGNOSTICS, PROTOCOL   8.1
DISPLAY-DECIMALS STATEMENT IN VGROUPS   4.1.1
DOCUMENTATION, BIBLIOGRAPHY   :29
DYNAMIC ELEMENTS   7.3.8
DYNAMIC VARIABLE ARRAY   7.3.4a
                         7.3.4
DYNAMIC VARIABLES   4.4
ECHOING COMMANDS TO THE TERMINAL   5.6
ECHOING COMMANDS TO THE TERMINAL, PREVENTING   5.7
EDIT MASKS   7.9.1.1
             7.3.6
EFFICIENCY CONSIDERATIONS   5.21.1
EFFICIENCY CONSIDERATIONS, IN COMPILED PROTOCOLS   10.7
                                                   10.4
EFFICIENCY CONSIDERATIONS, MONITORING A SEARCH   5.18
EFFICIENCY CONSIDERATIONS, PRELOADING PROTOCOLS   2.5
EFFICIENCY CONSIDERATIONS, SHOW SUBFILE MAP   5.21.2
EFFICIENCY TESTING, WITH $IOCOUNT VARIABLE   6.10.0
ELEMENT INFORMATION   7.3.7
ELOG   8.4.3
ELSE COMMAND   1.6
               5.8.1
               5.8
END CLAUSE, WITH WDSR COMMAND   5.11
ENDBLOCK STATEMENT   3.1.1
ENDWHILE   3.1.2
ENTRY COMMANDS SUBFILE   1.1
EVAL COMMAND   7.9.2
               7.1
               5.16
EXCLAMATION POINT PREFIX   5.7
EXECUTING A PROTOCOL   2.2
EXECUTING A PROTOCOL, IN THE ACTIVE FILE   2.1
EXPONENTIATION   7.4.1
EXPRESSIONS   5.1.1
              5.14
EXPRESSIONS, IN FUNCTIONS   7.1
EXPRESSIONS, MADE UP OF CONCATENATED STRINGS   5.1.1.1
EXPRESSIONS, MIXED TYPE   5.1.2
EXPRESSIONS, WITH THE IF...THEN COMMAND   5.8
EXPRESSIONS, WITH THE SET COMMAND   5.2
FORMATS TRACING, ERROR LOGGING   8.4.3
FORMATS TRACING, LOGGING   8.4.2
FORMATTING PROTOCOLS CODE   9.3
FUNCTION FOR DATETIME PROCESSING, $DATEOUT   7.2.9.4
FUNCTION FOR ENCODE/DECODE, $DENCODE   7.3.2a
FUNCTION FOR STRING MANIPULATION, $ASCII   7.2.2b
FUNCTIONS   1.4
FUNCTIONS FOR CONVERSION OF DATA TYPE, $CHAR   7.2.7
FUNCTIONS FOR CONVERSION OF DATA TYPE, $HEX   7.4.4
FUNCTIONS FOR CONVERSION OF DATA TYPE, $INTEGER   7.4.7
FUNCTIONS FOR CONVERSION OF DATA TYPE, $LINE   7.5.2
FUNCTIONS FOR CONVERSION OF DATA TYPE, $NEGATIVE   7.5.7
FUNCTIONS FOR CONVERSION OF DATA TYPE, $PACKED   7.5.9
FUNCTIONS FOR CONVERSION OF DATA TYPE, $REAL   7.6.7
FUNCTIONS FOR CONVERSION OF DATA TYPE, $RETYPE   7.7.1
FUNCTIONS FOR CONVERSION OF DATA TYPE, $STRING   7.8.1
FUNCTIONS FOR CONVERSION OF DATA TYPE, $TYPE   7.8a
FUNCTIONS FOR CONVERSION OF DATA TYPE, $TYPETEST   7.9.1
FUNCTIONS FOR CONVERSION OF DATA TYPE, $WDS   7.9.3
FUNCTIONS FOR CONVERSION OF DATA TYPE, SUMMARY   7.1.1
FUNCTIONS FOR CONVERSION TO DATA TYPE FLAG   7.4.2
FUNCTIONS FOR DATE PROCESSING, $DATEIN   7.2.9
FUNCTIONS FOR DATE PROCESSING, $DATEOUT   7.2.9.1
FUNCTIONS FOR DATE PROCESSING, $DAYS   7.2.9.2
FUNCTIONS FOR DATE PROCESSING, $WORKDAYS   7.9.3a
FUNCTIONS FOR DATE PROCESSING, $XDATE   7.9.3b
FUNCTIONS FOR DATE PROCESSING, $YYCALC   7.9.5b
FUNCTIONS FOR DATE PROCESSING, $YYTEST   7.9.5c
FUNCTIONS FOR DATE PROCESSING, SUMMARY   7.1.6
FUNCTIONS FOR MISCELLANEOUS TASKS, $BNF   7.2.3c
FUNCTIONS FOR MISCELLANEOUS TASKS, $EVALUATE   7.3.9
FUNCTIONS FOR MISCELLANEOUS TASKS, $GETELOG   7.4.2b
FUNCTIONS FOR MISCELLANEOUS TASKS, $ISSUECMD   7.4.8
FUNCTIONS FOR MISCELLANEOUS TASKS, $ISSUEMSG   7.4.8a
FUNCTIONS FOR MISCELLANEOUS TASKS, $NOLF   7.5.8
FUNCTIONS FOR MISCELLANEOUS TASKS, $SYSEVAL   7.8.3a
FUNCTIONS FOR MISCELLANEOUS TASKS, $SYSINFO   7.8.3b
FUNCTIONS FOR MISCELLANEOUS TASKS, $SYSTEM   7.8.4
FUNCTIONS FOR MISCELLANEOUS TASKS, $TEST   7.8.5
FUNCTIONS FOR MISCELLANEOUS TASKS, $XEQSTACK   7.9.4
FUNCTIONS FOR MISCELLANEOUS TASKS, SUMMARY   7.1.7
FUNCTIONS FOR NUMERIC MANIPULATION, $LOG   7.5.3
FUNCTIONS FOR NUMERIC MANIPULATION, $MOD   7.5.6
FUNCTIONS FOR NUMERIC MANIPULATION, $RANDOM   7.6.6
FUNCTIONS FOR NUMERIC MANIPULATION, $SQRT   7.7.7
FUNCTIONS FOR NUMERIC MANIPULATION, $TRUNC   7.8.8
FUNCTIONS FOR NUMERIC MANIPULATION, SUMMARY   7.1.4
FUNCTIONS FOR PACKED DECIMAL MANIPULATION, $DECIMAL   7.3.1
FUNCTIONS FOR PACKED DECIMAL MANIPULATION, $EDIT   7.3.6
FUNCTIONS FOR PACKED DECIMAL MANIPULATION, $EXP   7.4.1
FUNCTIONS FOR PACKED DECIMAL MANIPULATION, $PACKTEST   7.6.1
FUNCTIONS FOR PACKED DECIMAL MANIPULATION, $PRECISION   7.6.4
FUNCTIONS FOR PACKED DECIMAL MANIPULATION, $REMAINDER   7.6.9
FUNCTIONS FOR PACKED DECIMAL MANIPULATION, $UNEDIT   7.9.1.1
FUNCTIONS FOR PACKED DECIMAL MANIPULATION, $WINDOW   7.6.4
FUNCTIONS FOR PACKED DECIMAL MANIPULATION, SUMMARY   7.1.4
FUNCTIONS FOR STRING MANIPULATION, $BREAK   7.2.4
FUNCTIONS FOR STRING MANIPULATION, $CAPITALIZE   7.2.4a
FUNCTIONS FOR STRING MANIPULATION, $CASE   7.2.5
FUNCTIONS FOR STRING MANIPULATION, $CHANGE   7.2.6
FUNCTIONS FOR STRING MANIPULATION, $CHANGELIST   7.2.6a
FUNCTIONS FOR STRING MANIPULATION, $COMPARE   7.2.8
FUNCTIONS FOR STRING MANIPULATION, $DOUBLE   7.3.3
FUNCTIONS FOR STRING MANIPULATION, $ENCIPHER   7.3.8a
FUNCTIONS FOR STRING MANIPULATION, $INSERT   7.4.5a
FUNCTIONS FOR STRING MANIPULATION, $INSETC   7.4.6
FUNCTIONS FOR STRING MANIPULATION, $INSETL   7.4.6
FUNCTIONS FOR STRING MANIPULATION, $INSETR   7.4.6
FUNCTIONS FOR STRING MANIPULATION, $LEFTSTR   7.4.9
FUNCTIONS FOR STRING MANIPULATION, $LEFTSUB   7.5.1
FUNCTIONS FOR STRING MANIPULATION, $MATCH   7.5.5
FUNCTIONS FOR STRING MANIPULATION, $PARSE   7.6.2
FUNCTIONS FOR STRING MANIPULATION, $PARSESTRIP   7.6.2
FUNCTIONS FOR STRING MANIPULATION, $PMATCH   7.6.3
FUNCTIONS FOR STRING MANIPULATION, $RIGHTSTR   7.7.2
FUNCTIONS FOR STRING MANIPULATION, $RIGHTSUB   7.7.3
FUNCTIONS FOR STRING MANIPULATION, $RMATCH   7.7.3a
FUNCTIONS FOR STRING MANIPULATION, $RSTRIP, $LSTRIP   7.7.3c
FUNCTIONS FOR STRING MANIPULATION, $SIZE   7.7.5
FUNCTIONS FOR STRING MANIPULATION, $SPAN   7.7.6
FUNCTIONS FOR STRING MANIPULATION, $SQU   7.7.8
FUNCTIONS FOR STRING MANIPULATION, $STRIP   7.8.2
FUNCTIONS FOR STRING MANIPULATION, $SUBSTR   7.8.3
FUNCTIONS FOR STRING MANIPULATION, $TRANSLATE   7.8.7
FUNCTIONS FOR STRING MANIPULATION, $XSTR   7.9.5
FUNCTIONS FOR STRING MANIPULATION, $XSUB   7.9.5a
FUNCTIONS FOR STRING MANIPULATION, SUMMARY   7.1.2
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $DEFQTEST   7.3.2
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $ELEMINFO   7.3.7
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $ELEMTEST   7.3.8
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $ELIDINFO   7.3.7
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $ELIDTEST   7.3.8
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $ELNOTEST   7.3.8
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $FRAMETEST   7.4.2a
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $GETCVAL   7.4.3
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $GETIVAL   7.4.3
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $GETUVAL   7.4.3
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $GETVMATCH   7.4.3b
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $GETVOCC   7.4.3c
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $GETXVAL   7.4.3
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $INDEXINFO   7.4.5
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $INDEXNUM   7.4.5.1
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $INDEXTERM   7.4.5.2
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $LOOKSUBF   7.5.4
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $LOOKSUBG   7.5.4
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $LOOKSYS   7.5.4a
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $PATHFIND   7.6.2a
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $PATHINFO   7.6.2a
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $PRISMINFO   7.6.4a
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $PROCSUBG   7.6.5
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $RECINFO   7.6.7a
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $RECTEST   7.6.8
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $RESINFO   7.7.0
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $SEARCHINFO   7.7.4
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $SEARCHTEST   7.7.4a
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $SSW   7.7.9
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, $TRANINFO   7.8.6b
FUNCTIONS FOR SUBFILE AND DATA VALUE INFORMATION, SUMMARY   7.1.5
FUNCTIONS FOR TIME PROCESSING, $TIMEIN   7.8.6
FUNCTIONS FOR TIME PROCESSING, $TIMEOUT   7.8.6
FUNCTIONS FOR TIME PROCESSING, SUMMARY   7.1.6
FUNCTIONS FOR VARIABLE MANIPULATION, $AMATCH   7.2.1
FUNCTIONS FOR VARIABLE MANIPULATION, $APMATCH   7.2.2
FUNCTIONS FOR VARIABLE MANIPULATION, $ARMATCH   7.2.2a
FUNCTIONS FOR VARIABLE MANIPULATION, $ASET   7.2.3
FUNCTIONS FOR VARIABLE MANIPULATION, $ASORT   7.2.3a
FUNCTIONS FOR VARIABLE MANIPULATION, $ASORTX   7.2.3a
FUNCTIONS FOR VARIABLE MANIPULATION, $AUTHPARMS   7.2.3b
FUNCTIONS FOR VARIABLE MANIPULATION, $DYNASET   7.3.4
FUNCTIONS FOR VARIABLE MANIPULATION, $DYNAZAP   7.3.4a
FUNCTIONS FOR VARIABLE MANIPULATION, $DYNGET   7.3.5
FUNCTIONS FOR VARIABLE MANIPULATION, $DYNPUT   7.3.5
FUNCTIONS FOR VARIABLE MANIPULATION, $DYNZAP   7.3.5
FUNCTIONS FOR VARIABLE MANIPULATION, $GETPARMS   7.4.2c
FUNCTIONS FOR VARIABLE MANIPULATION, $REF   7.6.8a
FUNCTIONS FOR VARIABLE MANIPULATION, $SET   7.7.4b
FUNCTIONS FOR VARIABLE MANIPULATION, $SETPARMS   7.7.4c
FUNCTIONS FOR VARIABLE MANIPULATION, $STATGET   7.8.0
FUNCTIONS FOR VARIABLE MANIPULATION, $STATPUT   7.8.0
FUNCTIONS FOR VARIABLE MANIPULATION, $VARGET   7.9.1.1a
FUNCTIONS FOR VARIABLE MANIPULATION, $VARPUT   7.9.1.1a
FUNCTIONS FOR VARIABLE MANIPULATION, $VGROUPALTER   7.9.1.3
FUNCTIONS FOR VARIABLE MANIPULATION, $VGROUPINIT   7.9.2
FUNCTIONS FOR VARIABLE MANIPULATION, $ZAP   7.9.6
FUNCTIONS FOR VARIABLE MANIPULATION, SUMMARY   7.1.3
FUNCTIONS, OVERVIEW   7.1
GETTING RID OF A DYNAMIC ELEMENT   7.9.6
GETTING RID OF A DYNAMIC VARIABLE   7.9.6
GLOBAL VGROUPS   4.1
GLOBAL VGROUPS DEFINED   4.1.1
GOTO COMMAND   3.2.3
IF COMMAND   5.8.1
             5.8
IF...THEN COMMAND   1.6
                    5.8
ILLEGAL USAGE WITH DYNAMIC TYPE   :4.2.1.6.13
IN ACTIVE PREFIX   6.4.5
IN AREANAME PREFIX   6.4.5
IN PROTOCOL.NAME SUFFIX TO XEQ PROC COMMAND   3.2.1
IN-AREA STATEMENT IN FORMATS   6.4.5
INCONSISTENT STRING REDEFINITION   :4.2.1.6.11
INDEXED-BY STATEMENT IN VGROUPS   4.3
                                  4.1.1
INPROCS   7.6.5
INPUT COMMANDS, OVERVIEW   1.5
INTERRUPTING A SEARCH   5.18
ITERATE COMMAND   3.1.2
JUMP COMMAND   3.2.3
               1.7
LABEL STATEMENTS   1.7
                   3.2
LEAVE COMMAND   3.1.2
LENGTH STATEMENT, STATIC VARIABLES   4.1.1
LET COMMAND   5.1.1
              1.2
              5.17
LET COMMAND, WITH STATIC VARIABLES   4.2.2
LIMITING THE DURATION OF TERMINAL PROMPTS   6.19.6
                                            5.20a
LOAD PROTOCOL COMMAND   10.6
                        2.5
LOCAL VGROUPS   4.1
LOCAL VGROUPS DEFINED   4.1.1
LOCAL VGROUPS, DEFINING   4.1.0
LOCATOR CONVERSION FUNCTIONS   7.2.3.B
LOG, PROTOCOLS AND FORMATS TRACE   8.4.2
LONG VARIABLE HAS OCC > 1   :4.2.1.6.1
LOOPING BLOCKS   3.1.2
MULTIPLE COMMANDS   5.7a
MULTIPLE OCC VALUE MUST BE INDEXED   :4.2.1.6.10
N-TH ROOT OF PACKED DECIMAL VALUE, $ROOT   7.7.3b
NESTING BLOCK CONSTRUCTS   3.1.3
NESTING INTERNAL PROCEDURES   3.2.1
NESTING PROTOCOLS   2.2.1
NORMALIZE PACKED VALUES   7.5.8b
NULL STATEMENT ON THE ASK COMMAND   5.3
NULL VALUE, CONVERTING TO DIFFERENT TYPES   7.9.1
NUMERIC VALUE PROCESSING   7.8.8
                           7.6.6
                           7.6.4
                           7.6.9
                           7.7.7
OCC STATEMENT IN VGROUPS   4.1.1
OUTPROCS   7.6.5
OUTPUT COMMANDS, OVERVIEW   1.5
OVERVIEW, OF FUNCTIONS   7.1
PACKED DECIMAL FUNCTIONS   7.6.1
                           7.5.9
                           7.6.9
                           7.3.6
                           7.3.1
PASS PREFIX, WITH THE IF...THEN COMMAND   5.8.2
PASSING COMMANDS WITH THE ! PREFIX   5.7
PATH PROCESSING   7.6.2a
PAUSE COMMAND   5.15
PERCENT PREFIX, UNIX EMULATOR   5.8.3
PERFORM BUILD PROTOCOLS COMMAND, IN PROTOCOLS   9.1
PERFORM PRINT COMMAND   9.1
PFORMAT COMMAND   9.3
POUND SIGN   1.2
PRELOADING PROTOCOLS   2.5
PRISM APPLICATIONS, AND THE ASK COMMAND   5.3
PRISM APPLICATIONS, AND UNIVERSITY ID   6.20.7
                                        6.13.9
PRISM APPLICATIONS, PROTOCOL EXAMPLES FOR PRISM   1.2
                                                  1.1
PROCS IN PROTOCOLS   3.2.1
PROMPTING FOR INPUT   5.3
PROTOCOL DIAGNOSTICS   8.1
PROTOCOL EXECUTION OF THE ACTIVE FILE   2.1
PROTOCOL INVOCATION FROM A SPIRES FILE   2.2
PROTOCOL TRACING   8.4.3
                   8.4.2
                   8.4.1
PROTOCOL TRACING, ERROR LOGGING   8.4.3
PROTOCOL TRACING, GENERAL   8.4
PROTOCOL TRACING, LOGGING   8.4.2
PROTOCOLS, CONTINUING COMMAND LINES   5.1
PROTOCOLS, ONE-LINE   5.7a
PUBLIC PROTOCOLS, USING   9.2
RANDOM NUMBERS   7.6.6
READ-ONLY VARIABLES   7.9.1.3
                      4.1.1
READONLY STATEMENT IN VGROUPS   4.1.1
RECOMPILE COMMAND, AND STATISTICS   4.1.2
                                    10.7
RECOMPILE COMMAND, FOR PROTOCOLS   10.2
                                   10.1
REDEFINED VARIABLE CANNOT HAVE VALUE   :4.2.1.6.15
REDEFINES STATEMENT IN VGROUPS   4.1.1
REDEFINITION LOOPING PROBLEM   :4.2.1.6.12
REFERENCE COMMAND   7.4.3
REFERENCE VARIABLES   7.9.1.3
                      4.5.2
REPEAT...UNTIL   3.1.2
                 1.7
RESTORE SETTINGS COMMAND   6.1
RESTORE STATIC COMMAND   4.2.4
                         4.2.2
RESTORE TEMPORARY STATIC COMMAND   4.2.4
RETURN COMMAND   3.2.1
                 2.2.1
                 6.23.9
SECURE-SWITCHES   7.7.9
SEMICOLON DELIMITER   5.1
SEMICOLON DELIMITER, AND BLOCK CONSTRUCTS   3.1.3
SEPARATOR CHARACTERS   5.7a
SET ASK COMMAND   6.4.4
                  5.3
                  5.4
SET CLEAR COMMAND VARIABLES   6.5.0
SET COMMAND   1.2
              5.2
SET COMMAND, AND CONCATENATED STRINGS   5.1.1.1
SET COMPXEQ COMMAND   10.6
                      10.3
SET DEFQLOAD COMMAND   6.6.0
SET DELTA COMMAND   6.6.2
SET DIAG COMMAND   6.6.4
SET ECHO COMMAND   8.4.2
                   8.4
                   5.6
SET ELOG COMMAND   8.4.3
SET FTRACE COMMAND   8.4.2
SET LIST COMMAND VARIABLES   6.10.8
SET MESSAGES COMMAND, IN PROTOCOLS   5.9
SET NOCLEAR COMMAND VARIABLES   6.5.0
SET NOCOMPXEQ COMMAND   10.6
                        10.3
SET NODEFQLOAD COMMAND   6.6.0
SET NODIAG COMMAND   6.6.4
SET NOECHO COMMAND   5.6
SET NOLIST COMMAND VARIABLES   6.10.8
SET NOREAD COMMAND   6.2.1
                     6.1
SET NOSTOP COMMAND   5.10
                     2.4
SET NOTIMER PAUSE COMMAND   5.18
SET NOWRITE COMMAND   6.2.1
                      6.1
SET NOWYLBUR   5.8.2
SET NOXEQ COMMAND   2.2
                    10.6
SET NOXSTOP COMMAND   5.10a
SET PARM COMMAND   6.13.1a
SET PROMPT COMMAND   5.5
                     5.3
SET READ COMMAND   6.2.1
                   6.1
SET RECORD SEPARATOR COMMAND   6.14.6
SET SEED COMMAND   7.6.6
SET SEPARATOR COMMAND   5.7a
SET STOP COMMAND   5.10
                   2.4
SET SUBLATCH COMMAND   5.25
SET SUPERMAX COMMAND   6.18.4
SET SUPERVAL COMMAND   6.18.4
SET TIMER COUNTER COMMAND   5.18
SET TIMER PAUSE COMMAND   5.18
SET TIMER WAIT COMMAND   5.18
SET TLOG COMMAND   8.4.2
                   8.4.1
                   8.4
SET UPLOW COMMAND VARIABLES   6.21.2
SET UPPER COMMAND VARIABLES   6.21.2
SET VGROUP COMMAND   4.2.1
SET WDSR COMMAND   5.13
                   5.11
SET WDST COMMAND   5.13
SET WDSW COMMAND   5.13
SET WIDTH COMMAND   5.22
SET WRITE COMMAND   6.2.1
                    6.1
SET WYLBUR   5.8.2
SET WYLBUR COMMAND   2.4
SET XEQ COMMAND   2.2
                  10.6
                  10.3
SET XSTOP COMMAND   5.10a
SET XTRACE COMMAND   8.4.2
                     8.4.1
                     6.24.1
                     8.4
SHOW ALLOCATED COMMAND   4.2.3
                         8.2
SHOW COMPXEQ COMMAND   10.3
SHOW DYNAMIC VARIABLES COMMAND   4.4
                                 8.2
SHOW DYNVAR COMMAND   4.4
SHOW DYNVAR COMMAND, WITH TRIPLES   7a.13
SHOW ELOG COMMAND   8.4.3
SHOW EVAL COMMAND   5.14
SHOW FREE CORE COMMAND   5.21.1
SHOW FREECORE COMMAND   5.21.1
SHOW LOADED PROTOCOLS COMMAND   2.5
SHOW STATIC VARIABLES COMMAND   4.2.3
                                8.2
SHOW SUBFILE MAP COMMAND   5.21.2
SHOW SUBFILE SIZE COMMAND VARIABLES   6.18.0
SHOW TLOG DATA COMMAND   8.4.2
                         8.4.1
SHOW TRIPLES COMMAND   7a.13
SHOW XEQ STACK   3.2.1
SHOW XEQ STACK COMMAND   3.2.2
                         2.3
SHOW XTRACE COMMAND   8.4.1
SITE STATEMENT IN VGROUPS   4.1.1
SLASH PREFIX   1.3
               5.17
               5.14
               5.7a
SLASH PREFIX, INCORRECT WITH THE LET COMMAND   5.1.1
SLASH PREFIX, WITH THE IF...THEN COMMAND   5.8
SLASH PREFIX, WITH THE SET COMMAND   5.2
SPECIAL CHARACTERS IN SYSTEM FUNCTIONS   7.1
STAR COMMAND   5.14
STATIC SUBFILE   4.2.4
STATIC VARIABLE ARRAY   7.2.3
STATIC VARIABLE ARRAYS   4.3
STATIC VARIABLES   4.1.1
                   4.1
STATIC VARIABLES, LENGTH   4.1.1
STATIC VARIABLES, NAMES   4.1.1
STATIC VARIABLES, PREDEFINING VALUES   4.1.1
STATIC VARIABLES, REALLOCATING   7.9.1.3
STATIC VARIABLES, REDEFINING   4.1.1
STATIC VARIABLES, TESTING   7.9.1.2
STATIC VARIABLES, VALUE STATEMENT   4.1.1
STATISTICS FOR A COMPILED PROTOCOL   10.7
STATISTICS FOR COMPILED VGROUPS   4.1.2
STORE SETTINGS COMMAND   6.1
STORE STATIC COMMAND   4.2.4
                       4.2.2
STORE TEMPORARY STATIC COMMAND   4.2.4
STRUCTURAL OCCURRENCE MAP   7.4.3a
STRUCTURED PROGRAMMING   3.1
SUBROUTINES IN PROTOCOLS   3.2.1
SYS PROTO SUBFILE, FOR COMPILING PROTOCOLS   10.8
SYSTEM FUNCTIONS   1.4
SYSTEM FUNCTIONS, OVERVIEW   7.1
SYSTEM VARIABLES   5.2
SYSTEM VARIABLES, BY FUNCTION   6.3
SYSTEM-DEFINED VARIABLES   1.2
TESTING CONDITIONS   5.8
TESTING ENCIPHERED DATA   7.3.8a
THEN COMMAND   1.6
               5.8.1
               5.8
TLOG FOR TRACING   8.4.2
TRACE LOG   8.4.2
TRACING, PROTOCOL   8.4.3
                    8.4.2
                    8.4.1
TRACING, VARIABLE VALUES   8.4.2
                           8.4.1
TRIPLES   7a
TRIPLES, $ANY VARIABLE   7a.3
TRIPLES, $ATTRIBUTE FUNCTION   7a.8
TRIPLES, $GROUP FUNCTION   7a.9
TRIPLES, $GROUPELEMENT FUNCTION   7a.12
TRIPLES, $GROUPSIZE FUNCTION   7a.10
TRIPLES, $GROUPSORT FUNCTION   7a.11
TRIPLES, $LOOKUP FUNCTION   7a.7
TRIPLES, $MADE FUNCTION   7a.4
TRIPLES, $MAKE FUNCTION   7a.1
TRIPLES, $NEW VARIABLE   7a.2
TRIPLES, $OBJECT FUNCTION   7a.8
TRIPLES, $UNMAKE FUNCTION   7a.5
TRIPLES, $UNMAKETRIPLE FUNCTION   7a.6
TRIPLES, $VALUE FUNCTION   7a.8
TRIPLES, USAGE GUIDELINES   7a.14
TRIPLES, USE-COUNT   7a.6
TYPE STATEMENT IN VGROUPS   4.1.1
UNKNOWN VARIABLE TYPE   :4.2.1.6.4
UNLOAD PROTOCOL COMMAND   2.5
UNTIL   3.1.2
USE-COUNT, OF TRIPLES   7a.6
USER-DEFINED VARIABLES   1.2
VALUE INCONSISTENT WITH MULTIPLE OCCS   :4.2.1.6.8
VALUE MUST BE A 2 OR 4 BYTE INTEGER   :4.2.1.6.9
VALUE STATEMENT IN VGROUPS   4.3
                             4.2.4
                             4.2.2
                             4.1.1
VARIABLE ARRAYS   4.3
                  4.1.1
VARIABLE STATEMENT IN VGROUPS   4.1.1
VARIABLE SUBSCRIPTING   4.3
VARIABLE SUBSCRIPTING PROBLEM   :4.2.1.6.3
VARIABLE SUBSTITUTION   1.3
                        5.17
                        5.14
VARIABLE TABLE OVERFLOW   :4.2.1.6.14
VARIABLE TRACING IN PROTOCOLS   8.4.2
                                8.4.1
VARIABLE TYPE CONVERSION FUNCTIONS   7.1.1
VARIABLE VALUE ASSIGNMENT   5.1.1
VARIABLES   1.2
VARIABLES, CLASSES   4
VARIABLES, DISPLAY OF VALUES   8.2
VARIABLES, DYNAMIC   4.4
VARIABLES, OVERVIEW   4
VARIABLES, READ-ONLY   7.9.1.3
                       4.1.1
VARIABLES, REFERENCE   7.9.1.3
                       4.5.2
VARIABLES, STATIC   7.9.1.3
                    7.9.1.2
                    4.1
VARIABLES, TYPES   4.1.1
VGROUP TOO LARGE   :4.2.1.6.5
VGROUP VALUE -- TOO LONG OR TOO MANY   :4.2.1.6.7
VGROUP VALUE CONVERSION ERROR   :4.2.1.6.6
VGROUP, RECORD DEFINITION   4.1.1
VGROUPS SUBFILE   4.1.1
                  4.1
VGROUPS, ALLOCATING   4.2.1
VGROUPS, ARRAYS   4.3
                  4.1.1
VGROUPS, CLEARING   4.2.1
VGROUPS, COMPILING   4.1
VGROUPS, DEALLOCATING   4.2.1
VGROUPS, DESTROYING   4.2.5
VGROUPS, FOR PROTOCOLS   4.1.1
VGROUPS, GLOBAL   4.1.1
VGROUPS, GLOBALLY DEFINED   4.1
VGROUPS, INDEXING   4.3
VGROUPS, LOCAL   4.1.1
VGROUPS, LOCALLY DEFINED   4.1
VGROUPS, SETTING   4.2.1
VGROUPS, STORING VALUES   4.2.4
WDS COMMAND   5.12
WDSE COMMAND   5.12
WDSR COMMAND   5.11
WDSW COMMAND   5.12
WHILE...ENDWHILE   3.1.2
                   1.7
WITH TIMELIMIT PREFIX   6.19.6
                        5.20a
WITHIN INTERACTIVE PREFIX   5.19
WITHIN LOG PREFIX, IN PROTOCOLS   5.20
WITHIN PREFIX   5.19
WITHIN XEQ PREFIX   5.19
WRITE FILE LOG COMMAND   5.23
XEQ BREAK   2.4
XEQ COMMAND   2.1
XEQ FRAME COMMAND   2.3
XEQ FROM COMMAND, DETAILS   2.2
XEQ LEVELS, TRACING   3.2.2
                      6.23.9
                      7.9.4
XEQ PROC COMMAND   3.2.1
                   1.7
                   5.3
                   2.3
XEQ SETTINGS   10.6
XEQ STACK   3.2.2
XEQ USERPROC   2.3
ZAP PROTOCOL COMMAND   10.2.1
ZAP STATIC COMMAND   4.2.5
ZAP VGROUP COMMAND   4.2.5
ZAPPING A DYNAMIC ELEMENT   7.9.6
ZAPPING A DYNAMIC VARIABLE   7.9.6