******************************************************************
* *
* 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.
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.]
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).
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.
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.
The rest of this document covers the following topics:
- How to store and execute a protocol. (Including how to build your own protocol subfile.)
- Command facilities used in protocols for branching, testing conditions, debugging, storing a user's settings, etc.
- How to define and use variables and "variable-groups" (vgroups).
- How to use system variables, system functions and "triples".
- How to compile a protocol for greater efficiency.
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".
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:
- a brief introduction to variables [See 1.2.] and variable substitution [See 1.3.]
- a brief introduction to functions [See 1.4.]
- a discussion of commands for input and output [See 1.5.]
- a discussion of commands for testing conditions [See 1.6.]
- a discussion of commands to control the execution flow in a protocol. [See 1.7.]
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.
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".]
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.
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 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.]
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
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.
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.]
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.]
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.]
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.]
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
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)
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".
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.]
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.]
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.
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 [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 [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.
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.
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.]
During your SPIRES session, you can execute a protocol that is in your active file by issuing the following command:
-> XEQ [AT label.name] [USING line.range]
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.
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
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.
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.]
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:
XEQ PROC label.name IN protocol.name
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.]
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:
XEQ FROM protocol.name ..protocol.name [parameters list] XEQ [ AT label.name ] [ USING line.range ]
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.
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 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.]
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:
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.]
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.]
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.]
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.]
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:
BREAK XEQ
To resume execution of the protocol from command mode, the protocol's end-user would type:
CONTINUE XEQ
(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 can also be caused by some types of programming errors, and cause the following terminal prompt:
-Continue XEQ?
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.
-Continue XEQ? break X-> show eval $lastxeq Select Fliedef <---(command failed because of misspelling)
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.]
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.
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.)
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
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:
- First it checks all preloaded protocols to see if one of them matches the name specified.
- Next it checks the protocol subfile (if any) that was SET XEQ, looking for a record with the name specified.
- Finally it checks the protocol subfile(s), if any, that are SET COMPXEQ, checking them in the order in which they were set, looking for the protocol record specified. (If there's no record with matching name in any of these places, SPIRES raises an error condition.)
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.)
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:
- The basic construct BEGINBLOCK...ENDBLOCK specifies a simple block of related commands. [See 3.1.1.]
- The looping block constructs, REPEAT...UNTIL and WHILE...ENDWHILE, can execute multiple times, based on conditions that you specify as part of the block. [See 3.1.2.]
- The LEAVE and ITERATE commands offer further control over execution of a looping block construct. [See 3.1.2.]
- The XEQ PROC command nests (transfers control temporarily) to a labelled subroutine. [See 3.2.1.]
- The SHOW XEQ STACK command helps you trace nested execution. [See 3.2.2.]
- The JUMP (or GOTO) command branches (transfers unconditional control) to a label within the protocol. [See 3.2.3.]
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.
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.
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.
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.]
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.)
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
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.'
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 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.
The considerations below may seem obvious, but are probably worth mentioning:
- Remember that something must happen within a looping block to make its conditions true (for REPEAT...UNTIL) or false (for WHILE...ENDWHILE), or else the block will execute indefinitely. For instance, if a block begins with the statement WHILE #TOTAL < 10, some statement within the block will need to increment #TOTAL till it reaches 10. (Or alternatively, you could use the LEAVE statement to exit the looping block from the middle.)
- Make sure your specified condition(s) do not lead to unexpected infinite loops. (This is easier to do than it sounds!) For instance, the statement WHILE COUNTER < 5 would cause an infinite loop because, without the #-prefix, "COUNTER" is considered a string value that is "less than" the number 5.
- To exit a looping block from the middle, use the LEAVE command in preference to the JUMP command. In fact, JUMP should probably only be used in block constructs for special cases, such as branching to an exit point, as in the example shown earlier.
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.]
This section briefly discusses some additional features of block constructs:
- how block constructs can be nested, using comments for clarity;
- how block constructs can be chained;
- how a block construct "remembers" and restores the result of the condition test that invoked it.
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.]
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.
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.
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.
- None of the commands named above (including LEAVE and ITERATE) may be issued interactively, but only within a protocol or other SPIRES programming tool:
-> begin block <--not valid as interactive command -Allowed only in XEQ
- Of the commands listed above, only LEAVE and ITERATE may be used as part of an ATTN clause, END clause, or NULL clause. (For instance, BEGINBLOCK may not be used within an ATTN clause.) In addition, only LEAVE and ITERATE may be used after a RETURN statement or after a command prefix, such as WITHIN LOG:
Wdsr, End = 'BeginBlock' <--BEGINBLOCK is invalid in a clause
Ask Upper, Attn = 'Leave' <--LEAVE is allowed in a clause
- A statement that contains block construct commands may not also use the slash prefix. (If you need to perform variable substitution, use the $TEST or $EVAL function instead of the slash.)
Let T = '#X < #Y'
/While #T <--Invalid block construct
Let T = '#X < #Y'
While $Test(#T) <--This is valid
- Statements beginning with UNTIL, ENDWHILE, or ENDBLOCK may not be the object of IF, THEN, or ELSE statements. (If your goal is to exit a looping block construct from the middle, use LEAVE instead.)
If #Status = '' Then EndWhile <--Invalid command
If #Status = '' Then Leave <--Use this instead
- Block constructs can be nested within each other, but not interleaved:
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.]
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:
- The XEQ PROC command [See 3.2.1.] nests to a subroutine or procedure (Proc) named with the command, temporarily transferring control to that Proc. (When the Proc encounters a RETURN command, control is returned to the calling point.)
- The JUMP (or GOTO) command [See 3.2.3.] branches to the label statement named with the command, transferring control to the part of the protocol beginning with that label statement.
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.
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:
++label.name
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".
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.
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 ?.)
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.
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.
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:
* TEST Xeq Proc FIRST.PROC Show Eval 'Now Return to Command Level' Return ++FIRST.PROC Xeq Proc SECOND.PROC Return ++SECOND.PROC Break Xeq Return
Issuing the SHOW XEQ STACK command at the execution break calls up the following display:
-> ..test -Type CONTINUE XEQ to resume -> show xeq stack - BREAK XEQ - XEQ PROC SECOND.PROC - XEQ PROC FIRST.PROC - XEQ FROM TEST
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.
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:
JUMP label.name
or
GOTO label.name
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.
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:
- Differences between static (pre-defined) variables and dynamic variables, and why static variables are preferable for production applications;
- How to define a global "vgroup" (variable group) for your protocol's static variables, and how to compile the vgroup; [See 4.1, 4.1.1.]
- How to set and clear a vgroup during execution of your application (the DECLARE GLOBAL VGROUPS and ENDDECLARE commands, the SET VGROUP and CLEAR VGROUP commands, or ALLOCATE and DEALLOCATE); [See 4.2.1.]
- How to see what vgroups are currently set and what the current values of variables are (SHOW ALLOCATED and SHOW STATIC VARIABLES); [See 4.2.3.]
- How to store and restore values in a vgroup (STORE STATIC and RESTORE STATIC); [See 4.2.4.]
- How to destroy a vgroup when you're finished with it (ZAP VGROUP), or destroy a particular set of stored values (ZAP STATIC). [See 4.2.5.]
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.
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.
When converting the variables your protocol uses into static variables, you choose between two types of vgroups, each with its own construction procedure:
- global vgroups, whose variable values can be shared among protocols and formats when and while they are allocated; and
- local vgroups, whose scope is only the protocol or format in which they are used.
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:
- Step 1: Define the vgroup in your active file by creating a record describing the characteristics (e.g., type and length) of each of your variables.
- Step 2: Select the VGROUPS subfile in SPIRES and add your vgroup definition to the file.
- Step 3: Compile the record.
Here is the procedure shown in more detail:
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.]
-> spires -> select vgroups -> add
To update an existing vgroup definition you'd use the familiar TRANSFER and UPDATE commands.
-> 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.
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:
DECLARE GLOBAL VGROUPS ALLOCATE = ORV.GQ.JPR.TESTVARS; ALLOCATE = ORV.GQ.JPR.TESTVARS2, HIDDEN; ENDDECLARE
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 orv.gq.jpr.testvars -> let select = 'TEST' || $SELECT : : -> clear vgroup orv.gq.jpr.testvars
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.]
You can define a local vgroup to be used within a protocol two different ways:
- within the protocol itself, using the DECLARE VGROUP command; or
- within a SYS PROTO record, if you are compiling the protocol.
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.
* INTEREST
- Computes amount of each of M monthly payments on loan of
- amount A at interest rate I.
DECLARE VGROUP LOCAL.INTEREST
VARIABLE = AMOUNT;
TYPE = PACK;
VARIABLE = MONTHS;
TYPE = INT; VALUE = 360;
VARIABLE = INTEREST;
TYPE = PACK;
ENDDECLARE
++MORTGAGE
...
A local vgroup declaration begins with the DECLARE VGROUP command:
DECLARE VGROUP vgroup-name [, HIDDEN]
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:
ENDDECLARE
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)
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.]
"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.
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.
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.
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.
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:
N LINENUMBER INT1
You may have up to 256 different variables in a vgroup, with each variable array counting as a single variable.
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).
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.
This optional statement specifies the variable type. The choices are as you would guess from the chart above:
STRING INT (integer) REAL REF PACKED FLAG CHAR LINE HEX DYNAMIC
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.]
You can use this optional statement to document the variable.
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.
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.]
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,
VALUE = '', 26('ABC');
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.]
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.
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 = STS; SITE = CMS,TSO; <--variable available at CMS, TSO sites SITE = ~CMS,TSO; <--variable hidden at CMS, TSO sites
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".
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.
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.]
There are four ways to allocate a compiled global vgroup:
This section will describe the first and third methods.
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 command
- one or more ALLOCATE statements that identify the vgroups
- an ENDDECLARE command
The DECLARE GLOBAL VGROUPS and ENDDECLARE commands have no options.
The ALLOCATE statements look like this:
ALLOCATE = global-vgroup-name [, HIDDEN];
For "global-vgroup-name" it is best to use the form:
ORV.gg.uuu.vgroup-name
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:
DECLARE GLOBAL VGROUPS ALLOCATE = ORV.GQ.JNK.SPIKE; ENDDECLARE
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:
If #Reply Then BeginBlock Declare Global Vgroups Allocate = ORV.GQ.JNK.REPLYVARS; EndDeclare EndBlock
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).
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:
SET VGROUP global-vgroup-name
For "global-vgroup-name" it is best to use the form:
ORV.gg.uuu.vgroup-name
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.
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.
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.]
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:
-> select vgroups -> for subfile +> show keys all GQ.JPR.TESTVARS +>
You may see the names of only your vgroups in the VGROUPS subfile.
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.
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.]
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:
SHOW STATIC VARIABLES [OF full-vgroupname] [BRIEF] [LIKE stem]
(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.
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.]
-> set vgroup orv.gq.jpr.testvars -> show allocated -VGROUP: GQ.JPR.TESTVARS -> show static variables -VGROUP: GQ.JPR.TESTVARS ANSWER = A ANSWER::1 = B ANSWER::2 = C HEXINT = 00000000 INTEGER = 0 QUESTION = ->
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:
-> show static variables brief -VGROUP: GQ.JPR.TESTVARS ANSWER::0 = A - - - ANSWER::2 = A HEXINT = 00000000 INTEGER = 0 QUESTION =
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:
-> show static variables like a -VGROUP: GQ.JPR.TESTVARS ANSWER = A ANSWER::1 = B ANSWER::2 = C ->
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.
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:
(first session, logged on as gq.jnk) -> set vgroup orv.gq.jpr.recipe.examine -> let lastexamined = $date -> store static orv.gq.jpr.recipe.examine to lasttime -> clear vgroup orv.gq.jpr.recipe.examine (next session, logged on as gq.jnk) -> allocate orv.gq.jpr.recipe.examine -> restore static orv.gq.jpr.recipe.examine from lasttime -> select recipes -> /find date.added after #lastexamined -Result: 5 RECIPES -> in active type -> let lastexamined = $date (etc.)
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:
-> select static -> for subfile +> show keys all GQ.JNK.LASTTIME$GQ.JPR.RECIPE.EXAMINE +>
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.]
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:
- the values to be assigned to the variables differ from one session to the next, as in the example above;
- you want to store more than one set of values for different situations;
- different users will use your vgroup and will want to store their own values.
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:
ZAP VGROUP[S] vgroupname [SOURCE]
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:
ZAP STATIC storage.record.name OF vgroupname
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:
ZAP STATIC vgroupname ALL
To remove all sets of stored static variables that belong to any of your vgroups, issue the ZAP STATIC command in this form:
ZAP STATIC * ALL
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.
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:
CLASSPERIOD::1 0800-0915 CLASSPERIOD::2 0930-1045 CLASSPERIOD::3 1100-1215 (etc.)
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,
LET NUM::3 = 6003 (explicit subscript) LET NUM::#SUB = 400 (variable subscript)
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:
variablename::0 variablename
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:
VARIABLE = ITEM; OCC = 5, 8, 20; INDEXED-BY = AISLE, SHELF, LOCATION; TYPE = STRING; VARIABLE = AISLE; OCC = 1; LEN = 4; TYPE = INTEGER; VARIABLE = SHELF; OCC = 1; LEN = 4; TYPE = INTEGER; VARIABLE = LOCATION; OCC = 1; LEN = 4; TYPE = INTEGER;
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:
-> let aisle = 3 -> let shelf = 2 -> let location = 9 -> let item::index = 'Fyne-Maid Turkey Dip (5 lb. barrel)' -> /* #item::index * Fyne-Maid Turkey Dip (5 lb. barrel) ->
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:
VGROUP = GQ.JNK.TEST3D;
VARIABLE = NUMBER; TYPE = INTEGER; OCC = 2,2,2;
INDEXED-BY = A, B, C; VALUES = 1, 2, 3, 4, 5, 6, 7;
VARIABLE = A; TYPE = INTEGER; OCC = 1;
VARIABLE = B; TYPE = INTEGER; OCC = 1;
VARIABLE = C; TYPE = INTEGER; OCC = 1;
After the vgroup is allocated:
-> allocate test3d -> show static variables Static Group : GQ.JNK.TEST3D NUMBER = 1 NUMBER::1 = 2 NUMBER::2 = 3 NUMBER::3 = 4 NUMBER::4 = 5 NUMBER::5 = 6 NUMBER::6 = 7 NUMBER::7 = 0
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.
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.
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:
-> let abc = $INT(10) -> show eval $type(#abc) INT -> let abc = 10 -> show eval $type(#abc) STR -> let abc = 5+5 -> show eval $type(#abc) PACK
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.)
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.]
As suggested in the previous sections on static variables, dynamic variables can be declared explicitly in a vgroup definition, e.g.,
VARIABLE = TABLE; TYPE = DYNAMIC;
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:
- 1) when an array has an indefinite number of occurrences (and the range of possible occurrences is large, e.g., there may be one occurrence or 100, but you have no way of knowing ahead of time) and/or each occurrence has an indefinite length (and the range of possible lengths is similarly large); or
- 2) when you will use only widely separated occurrences of an array (e.g., you might place values only in occurrences 1, 50 and 5000-5020 of an array, but still need the large occurrence numbers.); or
- 3) when a vgroup is too large as defined. If a vgroup is too large when it is compiled, the error message VGROUP TOO LARGE -- TRY DYNAMIC TYPE will appear.
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:
LET TABLE = $ZAP;
or use the $DYNZAP function:
EVAL $DYNZAP(TABLE,0,'')
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 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.
Passing or sharing values among SPIRES protocols and formats can be done in several ways:
- Values can be passed via some system variables, such as $ASK
- Values can be passed via dynamic variables
- Values can be passed via a compiled global vgroup
- Values can be passed via the $SETPARMS and $GETPARMS functions
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.
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:
$SETPARMS(expression1, expression2, ...)
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:
$SETPARMS('01/01/90', #PayDay, $xdate($days(#PayDay) + 2) )
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:
EVAL $SETPARMS(#StudentIDnum,#Quarter)
The syntax of the $GETPARMS function is:
$GETPARMS(variable-name1, variable-name2...)
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:
$SETPARMS('01/01/90', #PayDay, $xdate($days(#PayDay) + 2) )
$GETPARMS(FirstOfYear, DepositDay, "WithdrawalDay::1")
This has the same effect as if three LET statements could have been executed:
LET FirstOfYear = '01/01/90' LET DepositDay = #PayDay LET WithdrawalDay::1 = $xdate($days(#PayDay) + 2)
(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.]
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:
Main Program Subroutine
---------------------------- ---------------------------------
let Cost = #Item+#Handling
let Taxrate = 0.0725
eval $setparms(#Cost,#Taxrate,$Ref(Total))
[go to SALES.TAX module.....]
[#Out variable is defined as
a reference variable]
*SALES.TAX
eval $getparms(In,Rate,Out)
let Out = #In * (1+#Rate)
[return to main program]
[#Total now automatically has
the value computed for #Out
in the subroutine.]
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:
$REF(static-variable-name[,READONLY])
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:
Let Amount = $Ref(Cost)
To make it read-only, you add the ReadOnly parameter to the $REF function:
Let Amount = $Ref(Cost,ReadOnly) or $SetParms($Ref(Cost,ReadOnly))
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.
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.
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).
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:
if (#loop1 = 0 and #loop2 = 0) then if $xtrace then \
eval $issuemsg('-Loops not in effect yet',Info)
perform prism screen page3, \
title='Optional information about the caller', \
ok, prev='jump page2', send='jump done'
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:
command ; comment e.g. /find name $ask ; this command performs the search requested above
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:
* text WDSW WDSE WDS
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:
-? stack "letters;numbers"
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.
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:
[IN area] - text
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.
Values may be assigned to a variable with a command of the form
LET variablename = expression
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:
Simple Assignment:
LET COUNT = #TEMP
LET MAXNO = $INTEGER(332) (assigns the integer '332')
LET MAXNO = 332 (assigns the string '332')
LET STRING = 'NOW IS THE TIME...'
Assignment Using an Arithmetic Expression:
LET COUNT = #COUNT +1
LET COUNT = (#NUMBER * #FRACTION) -1
Assignment Using a String Expression:
LET NEWSTRING = #OLDSTRING1 ', ' #OLDSTRING2
LET ELEMVALUE = #ELM ' + ' #VALUE ';'
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:
-> let part1 = ABC -> let part2 = DEF -> let both = #part1#part2'GHI' -> /* #both * ABCDEFGHI -> let both = #part1'#PART2'GHI -> /* #both * ABC#PART2GHI
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.
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:
Let Num3 = #Num1||#Num2
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.
Expressions in assignment statements that involve more than a single value such as
(#NUMBER * #FRACTION)
can be decomposed into one or more subexpressions of the form
variable-type1 binary-operator variable-type2
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.
type1 same as type2 ... this is the most straightforward
and efficient case.
Example: IF #STRING = #STRING2 THEN . . .
IF #INTEGER = #INTEGER2 THEN . . .
No conversion takes place in these examples.
type1 not same as type2, but type1 is STRING.
This case is allowable, and results in conversion
of type2 to STRING before the operation is
attempted.
Example: IF #STRING = #INTEGER THEN . . .
This causes #INTEGER (the value of INTEGER)
to be converted to type STRING.
type1 not same as type2, but type2 is STRING.
This case results in conversion of the type2
(STRING) variable to type1 before the operation
is attempted.
Example: IF #INTEGER = #STRING THEN . . .
If STRING is numeric, its value becomes type INT.
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:
-> let string = ABC -> let counter = #string + 3.1416 - Conversion error: STR TO PACK, value = 'ABC' - Not a legal or complete command -> let string = $REAL(0) -> let counter = #string + 3.1416 ->
The last command above succeeded because STRING was type REAL. The following command sequence will also succeed:
-> let string = 0 -> let counter = #string + 3.1416 ->
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.
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:
Form: Example: SET variablename [=] value SET ASK 'YES' SET variablename SET NOLIST SET variablenamel variablename2 ... SET NOSTOP NOECHO SET feature [=] value SET SCAN PREFIX value
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.]
If you want to set a system variable to the value of another variable, the SET command (unlike LET) must be preceded by a "/":
SET ASK #QUESTION is incorrect; /SET ASK #QUESTION is correct.
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:
-> let question = 'What is 5-3? ' -> /set prompt = #question -Unrecognized: is 5-3? -> /set prompt = '#question' -> show eval $prompt What is 5-3? ->
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.
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.
The full syntax of the ASK command is as follows:
[WITHIN TIMELIMIT n] ASK [UPPER] [PROMPT='literal string'] ... ... [NULL='statement'] [EXACT] [NOTRIM] [NOECHO] [ATTN='statement']
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:
-> set prompt = 'What do you like to read?' -> ask :What do you like to read? MEDIEVAL THEOLOGY MAGAZINES -> ask exact What do you like to read?FREDDY THE PIG BOOKS -> ask prompt='What else do you like to read?' :What else do you like to read? THE DIARY OF ANN PAGE -> show eval $prompt What do you like to read? ->
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:
* Press 'ATTN' for Help ASK PROMPT='Name? ' ATTN='XEQ PROC HELP1' LET NAME = $ASK ASK UPPER PROMPT='Phone? ' ATTN='XEQ PROC HELP2' LET PHONE = $ASK CLEAR XEQ ++HELP1 * Use the Form: Lastname, Firstname RETURN ++HELP2 * Use the Form: ABN-NNNN RETURN
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.
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.
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:
ASK PROMPT='How many cards? (CR if 52) ' NULL='SET ASK = 52' IF $ASK > 52 THEN SET ASK = 52 LET HANDS = $ASK / 7
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,
SET ASK = 'ABC' will work LET X = ABC /SET ASK = #X will work /SET ASK = $SUBSTR(#X,0,1) won't work
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.
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.
The ! character or exclamation point, prefixed to a command, has two different potential uses:
- Prefixed to a protocol statement, the ! guarantees that the statement will not be echoed to the terminal during execution, whether or not SET NOECHO is in effect. [See 5.6.]
- Prefixed to any command, the ! guarantees that the command will pass directly to SPIRES (or SPIBILD, if the command is issued in that program) without being seen by an intervening program such as WYLBUR.
The ! command prefix takes the following form:
! command
where "command" is a command, label or comment statement issued within a protocol, or else a command issued online.
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.]
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.]
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.
:command//command//command.....//command
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:
-> :select restaurant//show indexes//show formats -> :for tree where date > 1/79//stack all//sequence date//in active type -> :let n=1//++label///merge #n//let n=#n+1//if #n<50 then jump label
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:
-> :for tree where d > 1/79//stack all//sequence date//in active type -> stack all -Stack: 347 ITEMS -> sequence date -Stack: 347 ITEMS -> in active type ->
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:
SET SEPARATOR xx
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 "#=".
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 ....
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."
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.
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...
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 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
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:
THEN command ELSE command
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:
IF $SELECTED THEN * You have a subfile selected. THEN * I will find out what it is. ELSE * You do not have a subfile selected. ELSE * I will select one for you. ELSE SELECT DRINKS IF $SELECT = 'DRINKS' THEN * You have DRINKS selected. ELSE /* You have $SELECT selected but I will change it. ELSE SELECT DRINKS ELSE * Now you have DRINKS selected.
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:
* You do not have a subfile selected. * I will select one for you. * You have DRINKS selected.
If the user had a subfile other than DRINKS selected:
* You have a subfile selected. * I will find out what it is. * You have PUBLIC PROTOCOLS selected but I will change it. * Now you have DRINKS selected.
The display when the user already has DRINKS selected can be easily determined.
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 and ELSE commands can be created using the XEQ PROC facility. [See 3.2.1.]
++ASK.QUESTION ASK PROMPT 'Is the answer A, B, or C?' NULL='JUMP FIRST.QUESTION' ++FIRST.QUESTION IF #ANSWER THEN XEQ PROC ANSWER.CHECK ELSE * Please answer the question. IF #ANSWER ~= 'A' THEN JUMP ASK.QUESTION (etc.) ++ANSWER.CHECK IF #ANSWER = 'A' THEN * Correct. ELSE * Incorrect, try again. RETURN
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.]
The ! prefix is not allowed in front of the command that follows THEN or ELSE. [See 5.7.]
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:
-> ,allocate ...
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.]
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:
-> ,IF (CURRENT NE 20) ...
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:
Command> PASS IF (#A > 4 OR #B < 10) AND #C = 3 THEN ...
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:
IF(#X OR #Y) AND #Z.... <---incorrect spacing for
IF condition in SPIRES
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:
-> %vi active.file
Regardless of how "vi" terminates, this command will always succeed, even when "vi" is not recognized as a system 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:
SET [type] MESSAGES [TO|=] level-number
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:
-> select plant biology -Database charge: $6.00/hour, $10.00/1000 displays, $1.00 minimum -Continue select? ok -> clear select -Approximate database charge: $1.00 ->
Message levels 0 through 3 are available to control these messages, with level 2 the default. The messages are controlled by the following levels:
0 - No messages or prompt. 1 or 2 - Messages at SELECT and CLEAR SELECT time. 2 or 3 - CONTINUE SELECT prompt.
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
ASK PRUMPT='This will fail, because prumpt should be prompt'
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:
++GETFILE ASK PRO='WHAT IS THE GOAL-KEY VALUE? ' SET NOSTOP / TRANSFER $ASK IF $NO THEN / * $ASK IS NOT A LEGAL VALUE, TRY AGAIN. ELSE JUMP CONTINUE SET STOP JUMP GETFILE ++CONTINUE
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.]
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:
* MAIN.CODE SET XSTOP XEQ PROC SUB1 RETURN ++SUB1 * This is the code of the subroutine. Since the level * above has a setting of XSTOP and since this subroutine * has not SET NOXSTOP, the XSTOP setting is carried down. RETURN
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,
*MAIN.CODE SET NOXSTOP - Since that was the default, it did not need to be set SET NOSTOP XEQ PROC INSTRUCTIONS ... - The main code continues RETURN ++INSTRUCTIONS SET XSTOP * Here are some instructions on using this protocol. - The instructions continue... RETURN
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.
The WDSR command reads a line from the active file and places it in the variable $ASK. The full syntax of the command is:
WDSR [,NULL = 'command'] [,END = 'command']
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
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.
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.
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).
The WDSW, WDSE, and WDS commands write lines to the active file. The syntax is:
{WDSW|WDSE|WDS} string
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,
/ WDSE No SPIRES Files listed for $ACCOUNT.
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.
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:
SET WDSx [TO|=] {line #|(occurrence) [line range]}
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:
SET WDSR 3
will cause the next WDSR command to read line 3, or the first line following it if line 3 doesn't exist.
Or,
SET WDSW END
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:
If LAST is and DELTA is then END is 0.002 1.000 1.000 12.5 0.01 12.51 1.2 0.1 1.3 1.4 1.5 1.5 1.5 1.5 3.0 1.6 1.0 2.0
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:
SET WDSR (3)
causes the next WDSR command to read the third line in the active file.
SET WDSR (3) 5/L
causes the next WDSR command to read the third line within the explicit range 5/L.
SET WDSR (3) 'SPIRES'
causes the next WDSR command to read the third line containing the string 'SPIRES'.
SET WDSR 'SPIRES' 56 IN 5/L
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
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 (or, as it is called, the "star" command) is of the form:
[IN area] * string expression
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:
-> let today = $date -> * Today is #today * Today is #today -> /* Today is #today * Today is 11/06/85
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 * 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:
[IN area] SHOW EVAL expression
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 3*5
15
-> show eval $EXP(2,3)
8
-> show eval #x
ABC
-> show eval #x||$substr('abcdefghi',3,3)
ABCdef
->
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,
-> let variable = '#x' -> let x = 4 -> show eval #variable #x -> /show eval #variable 4
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:
-? show eval $version 85.08.04 GQ.PRD System -?
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.
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.
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:
PAUSE [text to be sent to the terminal]
At the terminal, the word PAUSE, followed by the text is typed. For example:
- PAUSE (Depress carriage return to continue.)
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.
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.
Example: EVAL $INT(#A)
IF $NO: JUMP ERROR
A similar evaluation could have been done with the $TYPETEST function: [See 7, 7.2.]
IF ~$TYPETEST(#A,INT) : JUMP ERROR
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.]
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:
ASK PROMPT='Enter the author''s name for your search' / FIND AUTHOR $ASK
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.
Certain rare uses of the LET statement involving a sort of double substitution may require a '/' prefix. Consider the following examples:
Example 1:
-> let X = time -> / let Y = $'#X' -> show eval #Y 16:49:26 <--The current value of the $TIME variable ->
Example 2:
-> let X = 1 -> let Y = '+' -> let Z = 2 -> / let A = #X #Y #Z -> show eval #A 3 -> let A = #X #Y #Z -> show eval #A 1+2 ->
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
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.
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
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.
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
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:
SET TIMER WAIT interval
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.
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:
WITHIN [INTERACTIVE|XEQ] command
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:
WITHIN INTERACTIVE SET SERIOUS MESSAGES TO 4
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.
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."
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:
WITH TIMELIMIT n command
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:
WITH TIMELIMIT 10 BROWSE FIRST ACCOUNT (This would place a 10-second time limit on a user's response to the prompt "-More?" while browsing values for the ACCOUNT index.) WITH TIMELIMIT 5 IN ACTIVE DISPLAY (This would place a 5-second time limit on such SPIRES prompts as "-OK to clear?" or "Which record?")
[The prefix has many possible uses in full-screen programming as well. See the manual "SPIRES Device Services" for more information.]
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:
WITH TIMELIMIT 20 ASK PROMPT= 'Which record?' NULL= "JUMP RECORD"
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.]
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.
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.
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.
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.
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.
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.
The SET SUBLATCH command sets the value of the system variable $SUBLATCH. [See 7.3.29d.] The syntax is:
SET SUBLATCH 'string'
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).
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.
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:
$TRACE/$NOTRACE $ECHO/$NOECHO $DIAG/$NODIAG $STOP/$NOSTOP $CLEAR/$NOCLEAR $LIST/$NOLIST $UPPER/$UPLOW $DEFQLOAD $INFOI/$INFOX $WARNI/$WARNX $SERI/$SERX $CHRI/$CHRX
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.
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.
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.
IF $SELECTED = 1 THEN ... IF $SELECTED = $TRUE THEN ... IF $SELECTED THEN ...
The following commands test if no subfile is selected:
IF $SELECTED = 0 THEN ... IF $SELECTED = $FALSE THEN ... IF ~$SELECTED THEN ...
The SHOW EVAL command always returns a "1" or "0" when used to display the value of a flag variable.
Integer type system variables return 4-byte integers when used in variable expressions.
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
SET TERMINAL = V77
the $TERMINAL variable actually hold "V77 " -- a four character value. When the following IF statement executes
IF $TERMINAL = V77 THEN * Terminal is V77
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,
IF 'V77' = $TERMINAL THEN * Terminal is V77
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".
Line variables hold values that represent line numbers.
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.
$CHRX, $CHRI $MSGNUM ($MNUM) $ELOGSCNT, $ELOGWCNT $NOLEV $ENUM, $SNUM, $PSNUM $SERX, $SERI $ERRCODE ($MSGCODE, $MCODE) $SORTCODE $INFOX, $INFOI $UCODE ($USERCODE, $UCD) $MSGLEV $WARNX, $WARNI
$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
$DISCHR $MINCHR $FILENAME $NEXTSLOT, $NEXTSLOTKEY $FILEDATE $SELCHR $FILETIME $SELECT $FORMAT $SELECTED $FRAME $SUBFSIZE $GETSPATH, $GETXPATH $SETFORMAT $GLOFORMAT $SLOT/NOTSLOT $GOALREC $TCOMMENT $KEY $TRANSFER
$LASTRESNUM $RESNUM $RELPOS $RESULT $RESCNT $SRCHSTAT ($FINDSTAT) $RESHIST $STACK $RESNAME $ZRESULT/$NZRESULT
$FORTYPE ($FORTYP) $PRTCNT $GXCOUNT, $GPCOUNT $PRTLVL $PATHKEY $PXCOUNT, $PPCOUNT
$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
$ACTNUM ($ACTNO) $WDSL ($WDSLINE) $AREANAME $WDSR $DELTA $WDST $NEXTWDSR $WDSW $NEXTWDSW
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.
$ANY $NEW
These variables are documented in the manual "SPIRES Formats."
$ABORT $LABEL ($L) $ATTN $OVERFLOW $FLINES $PARM $FLUSH $SETFORMAT $FORMAT $SKIPF $FRAME $SUPPRESS $FTRACE $TESTSUBG $GLOFORMAT
$CCOL $LROW $CROW $NCOLS $CUROCC ($CURROCC) $NROWS $ELEMID $SCOL $ELOCC $SROW $LASTOCC $REFERENCED $LCOL $TESTREF $LOOPCT $TRUEOCC
$CLEN ($VLEN) $PVAL $CVAL $ULEN $DEFAULT $UVAL
$FREC $LRGLCTR $LRGNXT $RECNUM ($RECNO)
$ENDATA $STARTCHAR $ENDCHAR $TESTFAIL $SKIPEL
$APROCERR $PROCERR $GPROCERR $WPROCERR
$EROW $PROTECT $ECOL $REPROMPT $CHANGED $TERMPAD $GCHANGED $GNCHANGED $NODATA
$SORTDATA $SORTKEND $SORTDEND $SORTKEY
$COLUMNS $MARGIN $COLWDTH $NEWCOL $CURCOL $NEWPAGE $FTRLEN $NOBREAK $HDRLEN $PAGECTL $LINEPP $PAGENUM ($PAGENO) $LLEFT $REPORT/$NOREPORT $LREC
These variables are documented in the manual "SPIRES File Definition."
$PASSDEL $SUBCODE $PROCVALUE ($PROCVAL,$VAL) $VELEM $PTRACE $VOCC $P1
$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.
$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.
$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.
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 ->
$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.
$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).
$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:
SET CHARGE MESSAGE [=] number
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.
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.
$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.
$COMPXEQ is a string variable that returns the name of the currently set COMPXEQ subfile. This may be different from the currently selected subfile.
$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.
$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.
$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.
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.]
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.
$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.
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.
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.
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.]
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:
LET NUMBER = 3 IF NUMBER = 6 THEN * Number is six IF $ELSE THEN * Number is not six
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:
LET NUMBER = 3 IF NUMBER = 6 THEN * Number is six ELSE * Number is not six
$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
-Element=ID: -Serious data error, code E35 -Error at or before line 1. -Update terminated, code=S261
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:
/ EXPLAIN E$ENUM
$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.
$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:
-063(I)-End of global FOR
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.
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.'
$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).
$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.
$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:
-> select restaurant -> find name = the -Request has no content -Zero result -> show eval $findstat 2 ->
Because the word "the" appears in the exclusion list of the NAME index, the search request "had no content."
-> select records -> find composer beethoven -Result: 25 Album(s) -> find name horace beethoven -Zero result -> show eval $findstat 1
The name "Beethoven" did appear in the index, but there were no pointer values in the sub-index for anyone named "Horace Beethoven".
$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.
$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
$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.
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
nnnn.nnnn.nnnn....
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".]
$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,
-> set global format orv.gg.abc.testform -> set format $prompt -> show eval $gloformat ORV.GG.ABC.TESTFORM -> show eval $format PROMPT
$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.
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:
$GXCOUNT - count of Global FOR records examined
(Global eXamined COUNT)
$GPCOUNT - count of Global FOR records processed
(Global Processed COUNT)
$PXCOUNT - count of elements or structures examined at the
current Partial FOR level
(Partial eXamined COUNT)
$PPCOUNT - count of elements or structures processed at the
current Partial FOR level
(Partial Processed COUNT)
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.
-> for adds where phone occurs > 0
+> skip last
+> show levels
Processed Examined
FOR ADDS 3 6
+> show eval $gpcount
3
+> show eval $gxcount
6
$INFOX and $INFOI are integer variables that control the presentation of SPIRES informational messages. They are set by a command of the form:
SET INFO MESSAGE [=] number
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.
$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.
$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".
$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.]
$KEY is a string variable that contains the value of the last record key used by SPIRES in the current session.
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.
$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:
-> ..protocol.name - Not legal command Continue XEQ? break X-> show eval $lastxeq SET LENGTH = $LENGTH + 1 X-> clear xeqs ->
(Note: the command was illegal because the SET command does not allow expressions.)
$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.
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.
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.]
$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.
$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.
The $MSGLIT variable is used by the system in the generation of some error messages. Its value cannot be set by the user.
$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:
-063(I)-End of global FOR
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.
$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:
-> show lines group af Line Account User name Port location 41 AF.ISH Group users Gandalf 11D-DH3.06 178 AF.ISH#2 Group users Gandalf 11A-DH1.10 -> show line 178 AF.ISH#2 Group users Gandalf 11A-DH1.10 -> show eval $multilog 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.
$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.
$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.]
$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.]
$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.
See under $YES/$NO.
$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.
$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:
-> set format $prompt (ring) name phone.number
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:
-> select funds, 1ABC999
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:
SET PARM = parm <-- command and UPROC = SET PARM = parm; <-- Uproc
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.
$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.]
$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.
$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.)
$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.)
$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".
$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:
-> set prompt 'Tell me the truth:' -> ask upper prompt = 'yes or no? ' :yes or no? no <--- user responds to this prompt -> show eval $prompt Tell me the truth: <--- $PROMPT remains as set
$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.
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.]
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.
$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.
$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:
-> show eval $recsep **** -> set record separator 'ADD;' -> show eval $recsep ADD;
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.
$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.
$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".]
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.
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:
-> select restaurant -> show eval $resname RECORD -> find name salernos -Result: 1 Restaurant -> show eval $resname Restaurant
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.
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.
$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.
$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.
$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.
$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.
$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.
$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.
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.
$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 "//".
$SERX and $SERI are integer variables that control the presentation of SPIRES serious error messages.
They are set by the command:
SET SER MESSAGE [=] number
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.
$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,
-> set format testform -> show eval $setformat TESTFORM
$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.)
$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.
$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.]
$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.
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.]
$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.
$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.
$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.
$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).
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:
-> set supermax 64k -> show eval $supermax -> 65536
$SYSTYPE is an integer variable that indicates what SPIRES program you are currently in. The three values this variable can have are:
8 = SPIRES 6 = FASTBILD 4 = SPIBILD
$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.]
$TERMINAL (alias $TERM or $TER) is a character variable that contains the first four characters of the ID of the logged-on terminal.
$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.
$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
LET TIME = $RETYPE($UTIME,INT) * 2
will return the integer number of seconds since midnight in #TIME.
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.
Most Recent Occurrence in SPIRES $TIMELIMIT _________________________________ __________ issuance of untimed SPIRES command 0 issuance of SPIRES command with prefix WITH TIMELIMIT n n successful completion of command within time limit 0 failure to complete command within the time limit <0
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.
$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.
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.
$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:
SELECT subfile <-- the subfile has secure-switch 10 set
TRANSFER record1 <-- $TRANSFER contains key for record1;
record1 is locked
REMOVE record2 <-- this clears both the lock and $TRANSFER
UPDATE <-- this command causes 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.
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:
IF #REMOVED = $TRUE THEN ... IF #REMOVED = $FALSE THEN ...
These are equivalent to:
IF #REMOVED THEN ... IF ~#REMOVED THEN ...
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.]
$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.
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.
ASK UPPER PROMPT = 'Yes or No? ' ASK UPLOW PROMPT = 'What is your name? '
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.
$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).
$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.
$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).
$VERSION is a character variable 24-characters long that represents the version of the SPIRES system being used. It is in the form:
date account System
where "date" is in the form "yy.mm.dd". In the online system, "account" is either:
- PUBLIC, representing the production system
- GQ.PRD, representing the pre-production system
- GG.TES, representing the test system
- GQ.TES, representing the development system
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.)
$WARNX and $WARNI are integer variables that control the presentation of SPIRES warning messages. They are set by a command of the form:
SET WARN MESSAGE [=] number
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.
$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:
-> show eval $wdsl 0 -> add using 1/22 -Added record 972 -> show eval $wdsl 22 ->
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.
$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.)
$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:
SET WDST END IF $WDST > 0 THEN .... <something is in the active file>
$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.)
$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.
$XEQ is a string variable that returns the name of the currently set XEQ subfile. This may be different from the currently selected subfile.
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.
$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:
If SET XTRACE was issued by itself, add 1 If SET XTRACE JUMP was issued, add 4 If SET XTRACE VARIABLES was issued, add 8 If SET XTRACE PROTOCOLS was issued, add 16
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.
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).
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.
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.
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:
-> find city alberta -Result: 5 Locations <-- $NZRESULT is true -> and state california -Zero result, previous result retained -Result: 5 Locations <-- now $ZRESULT is true
$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.
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:
LET NUM = $INTEGER(#X)
The general syntax for functions is as follows:
$functionname(argument-list)
"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,).
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.
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:
LET DAY = $LEFTSTR($DATEOUT($UDATE,6),3) (Here the $DATEOUT function returns the value of the system variable $UDATE converted to a string format -- the coded value 6 specifies which of several different formats will be used. Next the $LEFTSTR function creates a new substring from the first three characters of this converted value.)
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.)
Functions can be categorized, somewhat loosely, according to the kind of task that they accomplish:
$CHAR ($CHARACTER, $TRIM) $NEGATIVE ($NEG)
$FLAG $PACKED ($PACK)
$HEX $REAL
$INTEGER ($INT) $STRING ($STR)
$LINE $WDS
$RETYPE $TYPE
$REF $TYPETEST
$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
$AMATCH $GETPARMS, $SETPARMS
$APMATCH $REF
$ARMATCH $SET
$ASET $VARPUT, $VARGET
$ASORT, $ASORTX $VARTEST
$AUTHPARMS $VGROUPALTER ($VALTER)
$DYNASET, $DYNAZAP $VGROUPINIT ($VINIT)
$DYNGET, $DYNPUT, $DYNZAP $ZAP
$DECIMAL $RANDOM
$EDIT $REMAINDER
$EXP $SQRT
$LOG $TRUNC
$MOD $UNEDIT
$PACKTEST $WINDOW
$PRECISION
$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
$DATEIN $TIMEOUT
$DATEOUT $WORKDAYS
$DAYS $XDATE
$TIMEIN $YYCALC, $YYTEST
$ACCESSTEST $ISSUEMSG
$CHECK $NOLF
$CLRSUBF $SYSEVAL
$EVALUATE ($EVAL) $SYSTEM
$GETELOG $TEST
$ISSUECMD ($ISSUE) $XEQSTACK
$GETAREA $SETAREA
$PUTAREA
(Note: functions associated with Device Services
areas are documented in "SPIRES Device Services".)
$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.)
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.]
"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:
LET HOUR = $BREAK($TIME,':')
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:
IF $PMATCH($CAP($ASK),SEA?RCH,EXI?T) = 2 THEN JUMP EXIT
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
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)
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)
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
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.
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)
-------------------------------------------------- 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.
--------------------------------------------------
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:
$ACCESSTEST(extdef-record-key)
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.
--------------------------------------------------
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:
$AMATCH(input-string,array-name,lower-bound,upper-bound)
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
--------------------------------------------------
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:
$APMATCH(input-string,array-name,lower-bound,upper-bound)
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.]
--------------------------------------------------
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:
$ARMATCH(input-pattern,array-name,lower-bound,upper-bound)
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
-------------------------------------------------- Function: $ASCII Purpose: Convert EBCDIC to ASCII or visa-versa. Related procs: $ASCII.IN, $ASCII.OUT --------------------------------------------------
$ASCII(code,string)
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.
--------------------------------------------- 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:
$ASET(array-name,value,low,high) $ASET(array-name,value,low) $ASET(array-name,value)
"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.
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.
$BLDLCTR(block-num, trailer-num, type)
where:
block-num = the block number within the Residual data set.
trailer-num = the trailer number for the desired entry within the residual block
type = "L" or is a null value. The "L" argument signifies that the locator is being built for a file whose Residual data set has been designated as "LARGE".
[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.
$UBLDLCTR(locator, type)
where:
locator = a residual locator of type HEX. type = the locator type as explained above.
Consider the following examples:
-? Let Lctr = $BldLctr(12345, 6, ) -? Let LLctr = $BldLctr(12345, 6, L) -? Show Eval #Lctr' '#LLctr 00303906 000C0E46 -? Show Eval $Type(#Lctr)' '$Type(#LLctr) HEX HEX -? Show Eval $UbldLctr(#Lctr, ) 12345-6 -? Show Eval $UbldLctr(#LLctr, L) 12345-6
For additional information about loactors:
[EXPLAIN TYPE=LCTR STATEMENT RECORD-TYPE.]
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.]
---------------------------------------------------
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.]
$ASORT(array-name,sort-code,low,high) $ASORTX(array-name,index-array-name,sort-code,low,high)
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:
- ASCEND -- sort the array in ascending order. This is the default if no "sort-code" is specified. It can be abbreviated to A or ASC.
- DESCEND -- sort the array in descending order. It can be abbreviated to D or DESC.
- ASCEND.ELIM -- sort the array in ascending order, but "eliminate" duplicates (see below). It can be abbreviated to ASCEND.E.
- DESCEND.ELIM -- sort the array in descending order, but "eliminate" duplicates (see below). It can be abbreviated to DESCEND.E.
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:
- 1) delete the duplicates, leading to, say, 4 values (four non-null values plus one null)
- 2) sort the five, ending up with the null value being the first occurrence;
- 3) zap the values in the array, creating 10 null values;
- 4) place the 5 sorted values into the first 5 occurrences of the array;
- 5) return the integer value "5".
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:
ITEM::0 = '' SORTNUM::0 = 0 ITEM::1 = 'Bricks' SORTNUM::1 = 0 ITEM::2 = 'Avocado' SORTNUM::2 = 0 ITEM::3 = 'Chum' SORTNUM::3 = 0 ITEM::4 = 'Avocado' SORTNUM::4 = 0 ITEM::5 = 'Avocado' SORTNUM::5 = 0 ITEM::6 = '' SORTNUM::6 = 0 $ASORTX(ITEM,SORTNUM,Ascend.Elim) -> returns 4 ITEM::0 = '' SORTNUM::0 = 0 ITEM::1 = 'Bricks' SORTNUM::1 = 2 ITEM::2 = 'Avocado' SORTNUM::2 = 1 ITEM::3 = 'Chum' SORTNUM::3 = 3 ITEM::4 = 'Avocado' SORTNUM::4 = 0 ITEM::5 = 'Avocado' SORTNUM::5 = 0 ITEM::6 = '' SORTNUM::6 = 0
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.)
-------------------------------------------------
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]
---------------------------------------------- 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:
$BNF(Load,name,object) or $BNF(Tempload,name,object)
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) to get the n-th parameter's code. $BNF(Get,Value,n) to get the n-th parameter's value.
$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.]
-------------------------------------------------
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(input-string,string2)
$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:
LET PREFIX = $BREAK(#ACCOUNT,'.-/:')
Or suppose $ASK contains the value FIND AUTHOR CAPOTE and the statement below is executed:
LET CMD = $BREAK($ASK,' ')
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.
------------------------------------------ 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:
$CAP('first.year') returns FIRST.YEAR
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.
------------------------------------------------ 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:
$CASE(input-string,option-number)
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:
Number Option
0 Convert the entire string to uppercase
1 Convert the entire string to lowercase
2 Convert only the first character of each word in
the string to uppercase
3 Convert only the first character of each word in
the string to lowercase
4 Convert only the first character of the entire
string to uppercase
5 Convert only the first character of the entire
string to lowercase
10 Convert the entire string to lowercase, then convert
the first letter of each word to uppercase
11 Convert the entire string to uppercase, then convert
the first letter of each word to lowercase
12 Convert the entire string to lowercase, then convert
the first letter of the entire string to uppercase
13 Convert the entire string to uppercase, then convert
the first letter of the entire string to lowercase
For example, if #X has the value 'VIRGINIA is for LOVERS',
$CAP(#X) or $CASE(#X,0) returns 'VIRGINIA IS FOR LOVERS' $CASE(#X,1) returns 'virginia is for lovers' $CASE(#X,5) returns 'vIRGINIA is for LOVERS' $CASE(#X,10) returns 'Virginia Is For Lovers' $CASE(#X,12) returns 'Virginia is for lovers' $CASE(#X,13) returns '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".
----------------------------------------------- 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.
$CHANGE(input-string,string2,string3)
For example,
-> show eval $change(colour, ou, o)
color
-> show eval $change('$10','$','')
10
If string2 is null (or if string2 is longer than string1), then the input string remains unchanged.
----------------------------------------------- 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.
$CHANGELIST(input-string,string2,string3,string4,string5...)
For example:
-> show eval $changelist(hat,h,r,a,abbi) rabbit -> show eval $changelist(teeth,e,o,t,c,oc,nc) conch -> show eval $changelist($35.00,'$',,'.00',' dollars') 35 dollars -> show eval $changelist(abcdef,abc,def,ghi) -Incorrect parameter list ->
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.
------------------------------------------------ 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:
-> let x = 'This is a test. ' -> show eval $size(#x) 18 -> let y = $character(#x) -> show eval $size(#y) 15
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.
---------------------------------------- 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:
-> show eval $check(351,M) 4 ->
---------------------------------------- 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:
$CLRSUBF([&gg.uuu.filename] subfile-name)
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).
---------------------------------------- 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.
------------------------------------------- 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:
$COMPARE(string1,string2,character)
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,
$COMPARE(APPLES,ORANGES) returns '' (null string) $COMPARE(PEACHES,PEARS) returns PEA $COMPARE(GRAPE,GRAPEFRUIT) returns GRAPE $COMPARE(CLOVE,CLOVER,R) returns CLOVER $COMPARE(APPLE,A,P) returns APP
--------------------------------------------- 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:
$DATEIN(date-string[,options])
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:
-> show eval $date
11/19/85
-> show eval $datein($date)
19851119
-> show eval $datein(11/19/85) <--("/" treated as
00000000 division operator)
-> show eval $datein('11/19/85')
19851119
-> show eval $datein('12/35/1985') <--(error in date-string
00000000 causes "error" value)
-> show eval $datein('12/31/99',CCYY) <--(input value needs
00000000 four-digit year)
-> show eval $datein('01/01/01')
19010101
-> show eval $datein('01/01/01',Y25)
20010101
-> show eval $datein('na')
00000000
-> show eval $datein('na',NA)
99990909
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.
--------------------------------------------- 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:
$DATEOUT(hex-date)
or
$DATEOUT(hex-date,integer)
or
$DATEOUT(hex-date,option[,option...])
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:
-> show eval $dateout($datein('July 4, 1999'))
07/04/99
-> show eval $dateout($datein('July 4, 2000'))
07/04/2000
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.
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
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.)
--------------------------------------------- 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:
-> let DaysGone = $DAYS(#NewDate) - $DAYS(#OldDate)
In the example below, the user-variable #DayNum will hold an integer value representing the number of days elapsed since January 1, 1901.
-> let DayNum = $DAYS($UDATE) - $DAYS(19010101)
$DAYS can also be used to determine the day of the week of an input date string:
-> let y = $MOD($DAYS($DATEIN(#x)),7)
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.
--------------------------------------------- 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.]
$DATETIME(datetime_value)
$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.
INPROC = $DATETIME; INPROC = A28:8;
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:
7/1/2001
7/1/01
07/01/01
Sunday, July 1, 2001
Sunday, 1 July 2001
1-7-01 ("Canadian" dates)
2001.7.1 ("metric" dates)
The 1st of July in '01
1st day of 7/01
July 2001
7/01
7/--/01
2001
01
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:
today tomorrow yesterday +5 <- five days from today -3 <- three days ago Monday of this week Sunday of last week Sunday of next week 10th of last month last day of this month January 1 of last year 7th of this month +6 <- 7th day of the 6th month from this one September of next year next month this year
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.
--------------------------------------------- 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:
$DATEOUT(hex-datetime)
or
$DATEOUT(hex-datetime,integer)
or
$DATEOUT(hex-datetime,option[,option...])
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.
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
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.)
--------------------------------------------- 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:
$DECIMAL(value,decimal-places)
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.
------------------------------------------------ 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
$DEFQTEST(type)
where "type" is one of the following:
GOAL - determines whether there are any goal record entries
in the DEFQ (ADDs, UPDATEs, REMOVEs or DEQUEUEs) for
the subfile selected (or record attached).
REC - determines whether there are DEFQ record entries for
any record-type of the attached file, including the
goal record.
LOG - determines whether there are any log records or batch
requests in the DEFQ.
ANY - determines whether there is any data, regardless of
type, in the deferred queue (thus, includes all three
of the above types).
For the types listed above (other available types are described below), $DEFQTEST returns the following integer values:
1 - entries of the requested type are in the deferred queue 0 - entries of the requested type are not in the deferred queue
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:
SELECT RECORDS SELECT SELECTIONS $DEFQTEST(GOAL) = 1 $DEFQTEST(GOAL) = 0 $DEFQTEST(REC) = 1 $DEFQTEST(REC) = 1 $DEFQTEST(LOG) = 0 $DEFQTEST(LOG) = 0 $DEFQTEST(ANY) = 1 $DEFQTEST(ANY) = 1
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:
AUTO - determines whether the AUTOGEN flag is set for the file. JOB - determines whether the JOBGEN flag is set for the file.
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):
DATE - returns a date string in the form mm/dd/yy, representing
the most recent date that the deferred queue was
reinitialized
TIME - returns a time string in the form hh:mm:ss, representing
the most recent time that the deferred queue was
reinitialized
All types return a -1 integer value if no subfile is currently attached, or if an error occurs during function processing.
Another type is:
DISABLED - returns a value indicating whether the file has
been disabled by the file owner using the SET
SUBFILE DISABLED command
If the returned value is 0, then the subfile has not been disabled. Other returned values and their meanings:
1 = the subfile has been disabled for "attaches" (meaning
no one can select the subfile)
2 = the subfile has been disabled for updates
3 = the subfile has been disabled for access
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:
WRITE - determines whether the file is currently updateable.
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:
1 = the file is currently disabled by the file owner
2 = the file is currently being processed in SPIBILD
3 = the file system is in read-only mode, undergoing
maintenance
4 = an error occurred during $DEFQTEST processing
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:
PROCESS - determines whether the file is enabled for processing
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.
DUPLICATE - determines whether defq-duplication is being done
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
-------------------------------------------------- Function: $DENCODE Purpose: Encode or Decode values. Related procs: $ENCRYPT --------------------------------------------------
$DENCODE(item,key)
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.
let item = 'Some string to encode' let answer = $string($retype($dencode(#item,'key'),hex))
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.
let original = $dencode($retype($hex(#answer),string),'key')
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.
---------------------------------------------- Function: $DOUBLE Purpose: Duplicate a string value Related function: $CHANGE ----------------------------------------------
The $DOUBLE function has the following form:
$DOUBLE(input-string,string2)
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:
LET X = $DOUBLE(#X,'"')
In a Uproc within a format or USERPROC, the punctuation would be slightly different:
UPROC = "LET X = $DOUBLE(#X,'""')";
--------------------------------------------- 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:
$DYNASET(array-name,value,low,high)
"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.
---------------------------------------------
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:
$DYNAZAP(array-name,low,high)
"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.
-----------------------------------------------
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:
$DYNGET(variable-name,subscript,default-value) $DYNPUT(variable-name,subscript,assigned-value) $DYNZAP(variable-name,subscript,'')
"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:
LET X = $DYNPUT(Z,0,$INT(5))
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. ''.
----------------------------------------------- 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:
$EDIT(packed-value,edit-mask,divisor)
where
- packed-value is the value to be edited; it must be convertible to a packed decimal value (i.e., only a sign, a decimal point, an exponent sign (E) and digits are allowed).
- edit-mask is the edit-mask picture, following the conventions shown in the chapter on edit masks in "SPIRES Technical Notes".
- divisor is the power-of-ten divisor to be applied to the value before the value is processed through the mask. If this parameter is left off, it is assumed to be 0 (zero).
$EDIT returns a STRING value.
Some examples of the $EDIT function follow:
EDIT function Edited Result
--------------------------- -------------
$EDIT(12345,'$$,$$$.$$',2) " $123.45"
$EDIT('-1.00','$$$.$$CR') " $1.00CR"
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:
$EDIT(123456,'$$,$$$.99') = '$3,456.00'
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.
---------------------------------------------- Functions: $ELEMINFO, $ELIDINFO Purpose: Access element information for the current