****************************************************************** * * * 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 currently selected file Related function: $INDEXINFO ----------------------------------------------
The $ELEMINFO and $ELIDINFO functions return a value from the occurrence of one of the info-elements in an element information packet. You may include either the element name or element-id in the function.
The syntax of $ELEMINFO is:
$ELEMINFO(element-name,info-element,occurrence,default)
where "element-name" is the name or an alias of a real, virtual or phantom element defined for the selected subfile. It may be a fully qualified name (e.g. "str@str@elem").
The syntax of $ELIDINFO is:
$ELIDINFO(element-ID,info-element,occurrence,default)
where "element-ID" is the four-byte hex ID of the element. This ID can be found in the $ELEMID variable.
If the element named does not exist, the function returns a null value.
"Info-element" is the name or an alias of one of the info-elements. This parameter specifies what information you want to see from the element information packet. Possible values are:
NOTE MAXROWS, MAXROW DESCRIPTION, DESC VALUE-TYPE, VTYPE HEADING, HEAD INDEX COL-HEADING, COLHEAD USERINFO WIDTH, WID RDBMS_COLUMN ADJUST, ADJ RDBMS_DATATYPE INDENT RDBMS_DATALENGTH EDIT
Any value that contains a hyphen must be enclosed in apostrophes.
"Occurrence" is an optional parameter. It is a value, starting from 1, that says what occurrence of the specified information element is to be accesssed. (The only info-elements that are multiply-occurring are DESCRIPTION, COL-HEADING, INDEX, and USERINFO.) If you do not give a value, the function will retrieve the element's first occurrence.
The "default" parameter is also optional; it allows you to specify a value to be returned if the specified occurrence of the information element does not occur.
For example,
-> show eval $eleminfo(age,width,1,none)
will retrieve the "width" info-element for the element named "age". If no value exists for width, the function will return the string "none".
The example below shows one way $ELIDINFO and the $ELEMID variable could be used for accessing an element by its element-id instead of its name:
-> show eval $eleminfo(title,heading,1,none) Title of Book -> show eval $elidinfo($elemid,description,1,none) (Description info-element for TITLE element is displayed, if it exists)
Accessing an element by its element-id is more efficient than accessing it by name. [For more on the $ELEMID variable, see the manual "SPIRES Formats".] ;
-------------------------------------------------- Functions: $ELEMTEST, $ELIDTEST, $ELNOTEST Purpose: Retrieve information about a given element, such as its stored type or security --------------------------------------------------
The three functions $ELEMTEST, $ELNOTEST, and $ELIDTEST, help obtain information describing a named element in the selected subfile's goal record or in an accessed subgoal record.
The syntax is as follows:
$ELEMTEST(element-name,code) $ELNOTEST(element-number,code) $ELIDTEST(element-ID,code)
where "element-name" names an element in the selected subfile's goal or accessed subgoal. ("Element-number" refers to the element's numerical position within its record counting from zero, while "element-ID" refers to the element's four-byte hex elemid -- see the end of this section for more information on "element-number" and "element-ID".) If an element is not specified (i.e., if the first parameter is null for $ELEMTEST and $ELNOTEST; if the first parameter is $HEX(00000000) for $ELIDTEST), the first element in the file is assumed. The element named may be a dynamic element, with the following values for "code": TYPE, STRUCTURE, PART, and NAME.
Possible values for "code" are the same for all three functions, and are described in great detail below.
The function returns a null value if the element-name is not valid, if the code is illegal, or if no subfile is selected when the function is executed.
For $ELEMTEST (and $ELNOTEST and $ELIDTEST), the "code" parameter is what determines the information that the function supplies. Possible codes include the element's stored TYPE, its OCCURRENCE attribute in the file definition, its primary NAME and ALIASES, the SECURITY level set for it, and much more.
STRING - String INT - Integer STRUC - A structure REAL - Real LCTR - Record pointers XEQ - Type XEQ PACK - Packed decimal HEX - Any other type
SING - The element is singularly occurring. MULT - The element was not coded as singularly occurring.
FIX - FIXED section REQ - REQUIRED section OPT - OPTIONAL section VIR - VIRTUAL section DYN - The element is a dynamic element.
- UPDATE -- The element has no element-level security.
- SEE-ONLY -- The user may see values of already existing occurrences of this element but may not add, change or remove occurrences of it.
- MIXTURE -- This value means that the element is a structure containing at least one "update" element or structure and at least one element or structure that cannot be updated (it might be see-only or "hidden", meaning that it cannot be seen by you at all). It may also be called a "partial-update" structure.
O = A33 or one of the procs named above is coded in the structure's Outproc (the value is the letter O, not the number zero) I = A33 or one of the procs named above is coded in the structure's Inproc (the value is the letter I) IO = A33 or the procs named above are coded for both the structure's Inproc and Outproc '' = A null value indicates the element does not have A33 or one of the procs named above in its Inproc or Outproc, or else the element named in the function is not a structure, or else is not a valid element
General note about $ELEMTEST: If an element is hidden, then this function fails, since the element does not appear as an element in the selected subfile.
The $ELNOTEST function has the form:
$ELNOTEST(element-number,code)
where "element-number" is the number of the element within the record. For example, the first element of a goal record is element 0, the second is 1, and so forth. The first element within a structure is element 0. Elements within structures are identified by the element number of the structure, followed by an "@" sign, followed by the number of the element within the structure. Thus, the third element within the structure which itself is the second element in a record would be identified as "1@2". Note that because "@" is a special character, you must enclose the parameter value in quotation marks or apostrophes, e.g., $ELNOTEST('6@3',NAME).
The $ELIDTEST function has the form:
$ELIDTEST(element-ID,code)
where "element-ID" is a four-byte hex value identifying an element within a given record type. [Compare the $ELEMID variable, documented in the manual "SPIRES Formats".] Accessing an element by its element-ID is more efficient than using the element name, and therefore $ELIDTEST is used in general file formats such as $REPORT and $PROMPT, but elsewhere is used less commonly than $ELEMTEST or $ELNOTEST.
----------------------------------------- Function: $ENCIPHER Purpose: Return a random (enciphered) hex value from the input string value -----------------------------------------
The $ENCIPHER function takes an input string value and returns it as a coded (enciphered) hex value. Since there is no way currently known to decipher this hex value back into its original value, the function provides an effective method for enciphering data and hiding its original value.
The form of the function is as follows:
$ENCIPHER(input-value)
where "input-value" is the string value to be enciphered or hidden (for instance, a user's private ID). The function returns an 8-byte hex value (16 hex digits) that is a randomization of the value input. For a given input value, the function would always return the same enciphered value.
For example:
-> show eval $encipher(123) 4B7B2996CAE2E90F
$ENCIPHER should be most useful for storing and validating values (such as personal ID numbers) that are too private to be seen in uncoded form. For instance, an application prompting users for personal ID would have to "encipher" the value before storing it or testing it, so that owners of the application would never see the real value. But the application could still verify the ID, for instance, by performing a lookup in a central ID file, where the ID values would be stored in enciphered form. As long as these enciphered values match, the original ID values also match.
For methods of encrypting data through a file definition, EXPLAIN SET CRYPT COMMAND and EXPLAIN $ENCRYPT PROC.
------------------------------------------------ Function: $ENCODE and $DECODE Purpose: Encode/Decode strings ------------------------------------------------
The $ENCODE and $DECODE functions both $FUN records in RECDEF. $ENCODE takes a single string argument and returns an encoded result as a HEX-string. $DECODE takes the HEX-string and returns the original string that was encoded. For example:
-> show eval $encode(apple) AF4DC0AC04DCE97 -> show eval $decode(AF4DC0AC04DCE97) apple
What is interesting to note is that $ENCODE will return different answers for the same original string, but all of them will $DECODE back to the original string. Here's an example:
-> show eval $encode(apple) 5042427F3348099 -> show eval $decode(5042427F3348099) apple
As you can see, the HEX-strings are different in these two examples, but the original string is "apple" in both. The $DENCODE function is used by both $ENCODE and $DECODE, making use of a changing key.
----------------------------------------- Function: $EVALUATE ($EVAL) Purpose: Evaluate an expression -----------------------------------------
The $EVALUATE function evaluates expressions, much like the slash prefix ('/'), and is most useful within FORMAT or USERPROC coding where the '/' is not available. [See 1.3, 5.17.]
$EVALUATE is a single argument function of the form:
$EVALUATE(input-string)
The input string is assumed to be an expression consisting of variables, functions, operators, and other strings. $EVALUATE evaluates the expression, then returns this value converted to a string.
Some examples follow:
EVALUATE function Result ---------------------- ----------------- $EVALUATE("1+2") 3 $EVALUATE($ASK) 3 for $ASK=1+2
Note that in the second example, $EVALUATE first substitutes a value for the variable, then evaluates this value.
Keep in mind that $EVALUATE returns a string value:
-> if $eval(10+1) < 2 then * 11 is less than 2 * 11 is less than 2
The string "11" sorts before ("is less than") the string "2". You could fix this with the $INT function in this case:
-> if $int($eval(10+1)) < 2 then * 11 is less than 2 ->
[See 5.1.2.]
--------------------------------------------------------- Function: $EXP Purpose: Raise a packed value to the specified power Related functions: $LOG, $SQRT ---------------------------------------------------------
$EXP is a two-argument function of the form:
$EXP(packed-value,integer-value)
The first value is raised to the power specified by the "integer-value". For instance, $EXP(2,3) returns the value of 2 raised to the third power. The value returned by $EXP is of type PACKED.
Some examples of the $EXP function follow:
EXP function Result ------------------- ----------- $EXP(2,3) 8 $EXP(3.5,3) 42.875 $EXP(2,-1) 0.5
------------------------------------------------ Function: $FACTORIAL Purpose: Compute n! ------------------------------------------------
The $FACTORIAL function takes a single integer input, n, and computes n! creating a string answer that is acceptable as a packed value. n values larger than 27 show exponential notation. Negative input values unconditionally return 1. Example:
-> show eval $factorial(7) 5040
------------------------------------------------ Function: $FACTORS Purpose: Compute the prime factors of an integer ------------------------------------------------
The FACTORS function takes an integer argument, and the result of the function is a string of the argument's prime factors. If there are two or more such factors, they are separated by comma-blank. An individual factor may occur multiple times, represented by showing the factor, an apostrophe, and an integer number of times. For example, 8 has 2 as a prime factor three times, thus 2'3 represents the factor. Examples:
-> show eval $sysxeq(factors,40) 2'3, 5 -> show eval $sysxeq(factors,2275) 5'2, 7, 13
The FACTORS function uses primes up to 3571 to determine the factors of the input value. This allows for extremely large integers to be factored, and primes up to 12,666,481 can be determined. Values larger than that can also be processed if they have more than one prime factor.
------------------------------------------------ Function: $FLAG, $NOT Purpose: Convert value to type FLAG ------------------------------------------------
The $FLAG function has the following form:
$FLAG(argument)
Executing the function converts the argument to type flag, which has values of $TRUE and $FALSE (1 or 0).
Arguments of type STRING, CHAR and HEX convert to $FALSE if they are of null length. (Otherwise they convert to $TRUE.)
Arguments of type INTEGER, LINE, REAL and PACKED convert to $FALSE if they are 0. (Otherwise they convert to $TRUE.)
The $NOT function has the following form:
$NOT(argument)
Like the $FLAG function, $NOT converts the argument to type flag, but returns the opposite logical value compared to $FLAG. Thus, if $FLAG(argument) is $TRUE, $NOT(argument) is $FALSE, and vice versa.
$NOT can be used as a replacement for '~' in IF, WHILE, or UNTIL statements.
until $NOT(#y) ; instead of: until ~#y
------------------------------------------------- Function: $FRAMETEST Purpose: Retrieve information about frames in the currently set format -------------------------------------------------
The $FRAMETEST function is useful for determining the usage, direction, type, and dimensions of a frame within a currently set SPIRES format.
The function's syntax is as follows:
$FRAMETEST(frame-name,format-type,code)
"Frame-name" is the name of a frame of the currently set format. It may also be a negative integer indicating the positive ordinal into the frame table. Thus, -1 means the 1st frame, -2 the 2nd, etc.
"Format-type" is FILE (to test the frame of a file-specific format), GLOBAL (to test the frame of a global format). If you leave this option blank, FILE format is assumed.
"Code" is one of the the following set of values:
USAGE - to return the USAGE value of the frame DIRECTION - to return the direction of the frame TYPE - to return frame type ROWS - to return number of rows for the frame (from FRAME-DIM) COLS - to return number of columns for the frame (from FRAME-DIM) NAME - to return the frame's name (useful with negative integers)
If no FRAME-DIM statement is coded for a frame, the function returns a value of -1 for the ROWS and COLS codes. Note that the $FRAMETEST function cannot currently be used within a format. Also note that TYPE always returns a non-null answer unless there is no frame associated with the given frame-name or ordinal position.
----------------------------------------- Function: $GETELOG Purpose: fetch a piece of data from the error log -----------------------------------------
$GETELOG is a two-argument function that retrieves data stored in the error log when it has been established and initialized by a SET ELOG command. [See 8.4.3.]
The function's form is as follows:
$GETELOG(row-number, code)
where
- TEXT -- the entire text of the error message on that row
- ERROR -- either S (for Serious) or W (for Warning), depending on the error that generated that line of the log
- MNUM -- the message number of the message on that row ($MNUM)
- CURCMD -- the 3-letter abbreviation of the command being executed when the error occurred ($CURCMD)
- UCODE -- the value of $UCODE displayed as part of the message
- ENUM -- the value of $ENUM for that row
- SNUM -- the value of $SNUM for that row
- MSGLIT -- the value of $MSGLIT for that row
- MSGINT -- the value of $MSGINT for that row
Only the first four of these items are available for every row of the error log; the others are optional, and will return a null value if they do not exist for the requested row.
So, for example,
-> select restaurants -> set elog -> find date-added 2/30/93 -Element=DATE-ADDED: Invalid date -Serious data error, code=E31 -Not a legal or complete command -> find hrs daytime -Element=HRS: Illegal Input -Serious data error, code=E46 -Not a legal or complete command -> sho elog Element=DATE-ADDED: Invalid date -Serious data error, code=E31 Not a legal or complete command Element=HRS: Illegal Input -Serious data error, code=E46 Not a legal or complete command -> -> show eval $getelog(3,text) Element=HRS: Illegal Input -Serious data error, code=E46 -> show eval $getelog(2,curcmd) FIN -> show eval $getelog(1,enum) 31 -> show eval $getelog(5,text) ->
The last SHOW EVAL command above shows what happens when you request a line from the log beyond its boundary; at the moment, the log was only 4 lines long, so a null value was returned.
If you specify "row-number" as a negative integer, the function will help you locate rows containing the code-types you are looking for. Specifically, the function will convert the integer to a positive number (in other words, the absolute value of the negative integer) and begin looking in that row for the requested code. If it does not find an occurrence of the requested code, it will look in the next row of the error log, and so forth. When it does find an occurrence, the function returns the row number in which it appeared, not the value it found. To see the value it found, replace the negative row-number in the function with the returned value.
For example, continuing the session above:
-> show eval $getelog(-1,ucode) 1 -> show eval $getelog(1,ucode) Invalid date -> show eval $getelog(-2,ucode) 3 -> show eval $getelog(3,ucode) Illegal input -> show eval $getelog(-4,ucode) 0
In the example above, we start looking for message lines containing $UCODE. Starting with row 1 of the log (as indicated by "-1" in the function), we are told there's an occurrence in row 1. After we retrieve that occurrence ("Invalid date"), we tell SPIRES to start in row 2 (indicated by "-2") and look for the next one, which we are told is in row 3, and so on.
----------------------------------------- Function: $GETPARMS Purpose: assigns stored values from the $SETPARMS function to named variables Related functions: $SETPARMS, $REF -----------------------------------------
$GETPARMS is a multi-argument function that retrieves values saved internally by the $SETPARMS function. It is generally used to pass parameters between a program and subroutines.
The function's form is as follows:
$GETPARMS(variable-name1, variable-name2...)
where
--------------------------------------------- Functions: $GETUVAL, $GETCVAL, $GETIVAL, $GETXVAL Purpose: Retrieve element occurrences from a referenced record ---------------------------------------------
The $GETxVAL functions provide a way to access elements directly within records. The $GETUVAL and $GETIVAL functions retrieve unconverted, internal values, while the $GETCVAL and $GETXVAL functions retrieve values that have been converted into their external form (i.e., run through the element's Outprocs). The form and type of the value retrieved by any of these functions will match the form and type that the element's value has in its unconverted ($GETUVAL, $GETIVAL) or converted ($GETCVAL, $GETXVAL) form.
In a protocol, $GETxVAL provides a way to access element values for a record that has been brought into main memory with the REFERENCE command. These functions can also be used in Uprocs in formats and file definitions. See "Technical Notes," Chapter 11, for more details about the REFERENCE command; online, [EXPLAIN REFERENCE COMMAND.]
The differences between $GETUVAL and $GETIVAL, both of which retrieve the internal form of an element are:
- for virtual elements. The retrieved value of a virtual element from $GETUVAL is usually a useless value: if the virtual element redefines another element, the internal stored form returned by $GETUVAL is the internal form of that other element, which you could pick up directly; for other types of virtual elements, $GETUVAL returns a null value. On the other hand, $GETIVAL executes the virtual element's Outproc and then Inproc rules to create the returned value.
- when filters are in effect. By default, $GETUVAL will not apply any filters that are set, while $GETIVAL will. With either function, you can override the default with the "filter" option (see the syntax below).
The only difference between $GETCVAL and $GETXVAL is the filtering, as described above.
In sum, what this means is that $GETIVAL and $GETXVAL are generally more useful than the older $GETUVAL and $GETCVAL functions because the new functions take any filtering into effect by default (which is usually what you want) and $GETIVAL handles virtual elements the way you'd expect.
The four functions all have similar argument lists:
$GETUVAL(elem-name,occ-number,default-value,structural-occ-map,filter) $GETCVAL(elem-name,occ-number,default-value,structural-occ-map,filter) $GETIVAL(elem-name,occ-number,default-value,structural-occ-map,filter) $GETXVAL(elem-name,occ-number,default-value,structural-occ-map,filter)
"Elem-name" is the name of the element you want to access. This is the only required parm; if no other parms are included, the function returns the value of the first occurrence of the named element, or a null value if the element does not occur. To identify non-unique elements by name, use the special naming convention described at the end of this section. [The element named in "elem-name" may come from a phantom structure, with this restriction: you cannot use the "structural-occ-map" parm to navigate to an element in a phantom structure; also, a SPIRES format is more efficient than $GETxVAL for retrieving multiple elements from a phantom structure.]
"Occ-number" specifies which occurrence of "elem-name" you wish to retrieve. The occurrence number is "0" for the first occurrence, "1" for the second, "2" for the third, and so forth; if you do not specify an occurrence, SPIRES will retrieve the first occurrence. The "occ-number" may be affected by the FILTERED or UNFILTERED parm, described further below.
"Default-value" is a value that you would like returned if no value is retrieved from the record. If no value is retrieved and you do not specify a "default-value", the value returned is null.
The complicated "structural-occ-map" is used primarily in file definition Userprocs for traversing nested structures -- formats use indirect frames and protocols use partial processing to accomplish the same task in a more straightforward way. Basically the parm tells which occurrence of each structure is to be used when accessing elements in nested structures; it describes the route along which the record should be traversed to arrive at the specified element.
For example, in a file designed with two nested structures, STRUC1 containing STRUC2, a value of "1.3" tells SPIRES to retrieve an element occurrence only if it lies within the second structural occurrence of STRUC1 and (within that structural occurrence) only if it lies within the fourth structural occurrence of STRUC2. If you code "ANY" for the value, the function will ignore all structural boundaries and retrieve the "occ-number" occurrence of the element, counting all occurrences across all structures. You cannot use the "structural occurrence map" parm on a $GETxVAL function to navigate within nested structures in a phantom structure.
More information about this parm, and about techniques for saving the path navigated using the $GETSPATH or $GETXPATH variable, can be found right after this section [See 7.4.3a.] and in the manual "SPIRES File Definition".
The "filter" parm (whose values, FILTERED or UNFILTERED, can be abbreviated to F or U or any length in between) specifies whether any element filtering you have specified for display with the SET FILTER command should also affect the function when it retrieves an occurrence. If you do not code this parm, the $GETUVAL and $GETCVAL functions ignore any filters that you have set; the $GETXVAL and $GETIVAL functions will pay attention to filters by default.
Important note: the FILTERED parm affects both the "occ" parm and the "structural-occ-map" parm -- for instance, if you were to filter out the first occurrence of an element with the SET FILTER command, a $GETCVAL or $GETUVAL function with an "occ" parm equal to 0 would retrieve the second "real" occurrence as though it were the first occurrence. Element filtering is described in the "Technical Notes" manual, or online, [EXPLAIN FILTERS.]
The following example illustrates a simple use of the $GETXVAL function to retrieve the value of a record-level element:
-> reference 129 -> show eval $getxval(name,3,'No name') Pinky Dew -> show eval $getxval(name,4,'No name') No name
The first SHOW EVAL command returns the value of the fourth occurrence of the NAME element. The second SHOW EVAL command tries to retrieve the fifth occurrence, but no such occurrence exists, so the default value specified in the function is returned.
In a protocol, you would probably use the $GETxVAL functions in an IF or LET command. For example,
LET NAME = $GETCVAL(NAME,0,'No name') or IF $GETCVAL(NAME,0,'No name') = 'No name' THEN JUMP END
Note that with the first parameter to these functions, "elem-name", if the element named is within a structure and the name is not unique, SPIRES will retrieve values from the first element with that name (the first element encountered in the list shown by the SHOW ELEMENT NAMES command). To specify which element you want in the case of non-unique element names, use the "@" to designate the structural path. This form is common throughout SPIRES in situations where you need a particular structural element. Its syntax looks like this:
structure-name@structure-name...@element-name
where, from left to right, each structure named is nested within the previous one. There must be no blanks around the "@", and you must enclose the entire value in apostrophes, because "@" is a special character. Note that you only need to specify as many structure names from the right as are needed to uniquely identify the desired path.
You can also specify the element you wish to access by using a four-byte hex value representing the "element ID". This value is stored in the $ELEMID variable, which is set each time a GETELEM is executed in a format or an $ELEMTEST or $ELNOTEST function is executed. See the discussion of $ELEMID in the manual "SPIRES Formats" for more information.
If you attempt to use $GETCVAL or $GETXVAL on an element that is a structure processed by $STRUC.OUT or A33, the function may retrieve a null value if the structure is at the current referenced level; that is, if the structure is at the record level and a $GETCVAL/$GETXVAL is done on that element, the output processing usually done on the values in the structure to concatenate them into a single value may not occur. But if the structure is at a lower level, the values of the elements making up the structure will be retrieved and processed by the $STRUC.OUT proc (or A33 action). The same thing applies to structures being referenced during Partial Processing: if the structure has been referenced or can be specified in a Partial Processing FOR command, the $STRUC.OUT proc may not be executed when a $GETCVAL function is applied to that element, and the function may return a null value.
To access an element from within nested structures, when one or more of the nested structures is multiply-occurring, you may have to tell the $GETxVAL function exactly how to navigate to the particular structure occurrence that contains the element occurrence you want. The fourth argument on the $GETUVAL, $GETCVAL, $GETIVAL and $GETXVAL functions, the "structural occurrence map", lets you do this. It is also used in a similar way with the $GETVOCC function.
If navigation of structures is occurring naturally, such as during the display of a record, and you enter a USERPROC to process an element, $GETxVAL can access any elements within the containing structures, down to and including the record level, without using structural occurrence map.
Here is an example using structural occurrence map:
$GETCVAL(NAME,3,'None found',1.2.2)
retrieves the fourth occurrence of the element NAME, but only if it occurs in a structure along the occurrence route described by "1.2.2". Consider the following "show element characteristics" map:
Sec Occ Len Type St/El Element --- ---- --- ------ ----- ------- Req Sing String 00/00 key REC01 Opt Mult String 00/01 PART Opt Mult Struc 00/02 P1 Req Sing String 01/00 key . PRODUCT.ONE Opt Mult Struc 01/01 . P2 Req Sing String 02/00 key . . PRODUCT.TWO Opt Mult Struc 02/01 . . P3 Req Sing String 03/00 key . . . PRODUCT.THREE Opt Mult String 03/01 . . . NAME
The "1.2.2" map specifies the second occurrence of P1 and, within that structure, the third occurrence of P2 and, within that structure, the third occurrence of P3. Remember, occurrence numbers begin with zero (0). SPIRES will travel along that route, and when it gets to the third occurrence of P3, it will look in that structure for a NAME element and return the fourth occurrence of it, if one exists.
If you do not include the occurrence number as the second argument of the function, SPIRES will by default retrieve the first occurrence of that element within the structure.
It is important to remember that if you code the FILTERED parm as part of a $GETCVAL or $GETUVAL function, any filter information that you have previously established with a SET FILTER command will affect the traversal specified by the structural occurrence map. The $GETIVAL and $GETXVAL functions automatically apply filters. (However, filters can be turned off for those two functions by adding the UNFILTERED parm.)
You can specify a more deeply nested structural hierarchy than necessary; SPIRES will ignore the part of the map that it doesn't need. For example, the fourth occurrence of the NAME element shown above could just as well have been specified as follows:
$GETCVAL(NAME,3,'None found',1.2.2.0.0.1)
Once SPIRES has found the requested element along the route described, it goes no further along the specified route.
The structural occurrence map parm on the $GETxVAL functions cannot be used to retrieve elements from phantom structures; you would need to write a customized format to navigate to these "phantom" occurrences.
When the $GETxVAL function is executed, SPIRES remembers the structural occurrence map that it has just navigated to retrieve the element, and saves this information in the $GETSPATH and $GETXPATH variables. [See 6.4.] (SPIRES saves this information whether or not you have used the fourth argument of the function.) Note that the saved information represents an unfiltered path, even if filtering is in effect when the path is traversed. Therefore, the use of a structural occurrence map parameter in a subsequent $GETxVAL function may be incompatible with filtering.
The occurrence map information is available in both string ($GETSPATH) and hex ($GETXPATH) form and may be specified in either form. Using the string form, you could enter the value as "1.2.0.0"; it would be returned by $GETSPATH as '0001.0002.0000.0000'. $GETXPATH would return the same value as '0001000200000000' (the string representation of the 8-byte hex value). The value of both system variables is updated with the next use of either function, so you should save its value off in a variable of your own, if you will want to use it to force retrieval along the same (unfiltered) route later.
The sample record below may help show how the structural occurrence map relates to a particular record. Note that the structure containing occurrences of PRODUCT.THREE is nested within the structure containing PRODUCT.TWO, which in turn is nested within the structure containing PRODUCT.ONE:
REC01 = 3; PART = Gums; PRODUCT.ONE = Storax; PRODUCT.TWO = Adhesives; PRODUCT.TWO = Chewing Gum; PRODUCT.ONE = Resins; PRODUCT.TWO = Gum Rosin; PRODUCT.THREE = Varnishes; PRODUCT.THREE = Soaps; PRODUCT.THREE = Paper Sizes; PRODUCT.TWO = Gum Turpentine; PRODUCT.THREE = Paints, Stains; PRODUCT.THREE = Wood Fillers;
After you bring the record into main memory with the REFERENCE command, you can retrieve particular element occurrences from the record:
-> reference 3 -> show eval $getcval(product.three,1,'*',1.0) Soaps -> show eval $getcval(product.three,1,'*',1.1) Wood Fillers
Both retrievals are of the 'second' occurrence of the element PRODUCT.THREE, but according to a different structural occurrence map in each case. In this example, the route to the value "Wood Fillers" navigated into the second occurrence of the outermost structure (PRODUCT.ONE) in the record, and then, within that occurrence of PRODUCT.ONE, navigated to the second occurrence of the nested structure (PRODUCT.TWO).
If PRODUCT.THREE was the key of another nested structure, you could have done the following:
-> reference 3 -> show eval $getcval(product.three,0,'*',1.0.1) Soaps -> show eval $getcval(product.three,0,'*',1.1.1) Wood Fillers
You can also specify "ANY" as a value for the fourth argument of the function. This says that you want the nth occurrence of the element, no matter what route it appears on. SPIRES will search each occurrence of each structure until it finds the specified occurrence. Here is an example using the same sample record as above:
-> show eval $getcval(product.three,3,'*',1.1) * -> show eval $getcval(product.three,3,'*',ANY) Paints, Stains
After the first command, the default value, '*', is retrieved because there was no fourth occurrence of the PRODUCT.THREE element within the first occurrence of the second structure. But after the second command, the ANY parameter causes the function to navigate straight down into the record, disregarding structural boundaries.
Because static variables of type HEX are fixed length and $GETXPATH is variable length, if you assign the value of $GETXPATH to a compiled variable, the value is padded to the left with zeroes. This can cause unpredictable results, so it is recommended that you use $GETSPATH and static variables of type STRING when you will be assigning the structural occurrence map information to compiled variables. In cases where the length of the value is predictable and will not change, $GETXPATH offers a slight efficiency gain over $GETSPATH.
--------------------------------------------- Functions: $GETVMATCH Purpose: Retrieve the number of an element occurrence that matches a given string ---------------------------------------------
The $GETVMATCH function compares a given string against element occurrences within a referenced record, stopping when it finds a match and reporting the occurrence number. This function works similarly to the $GETxVAL functions: the argument list is similar, and the method of accessing the data is the same. It is similar in function to the $ARMATCH function, matching a pattern against an array of values.
$GETVMATCH has the following argument list:
$GETVMATCH(elem-name,occ,pattern,structural-occ-map,filter)
where
[More information about this parm, and about techniques for saving the path navigated using the $GETSPATH or $GETXPATH variable, can be found after the section on the $GETXVAL function [See 7.4.3a.] and in the manual "SPIRES File Definition".]
specifies whether any element filtering you have specified for display with the SET FILTER command should also affect the function when it retrieves an occurrence. The allowed values, FILTERED or UNFILTERED, can be abbreviated to F or U or any length in between. FILTERED is the default if you do not code this parm.
Important note: the FILTERED parm affects both the "occ" parm and the "structural-occ-map" parm -- for instance, if you were to filter out the first occurrence of an element with the SET FILTER command, a $GETVMATCH function with an "occ" parm equal to 0 would start at the second "real" occurrence as though it were the first occurrence. Element filtering is described in the "Technical Notes" manual, or online, [EXPLAIN FILTERS.]
The function returns an integer value as follows:
- zero or a positive integer = the number of the next occurrence of the element that matches the string, counting from 0 for the first occurrence. Remember: The value is affected by the occurrence number, filter and structural-occurrence-map arguments.
- -1 (negative 1) = no values of the element matched
- -2 (negative 2) = the record was not available, or some error occurred
The $GETVMATCH function sets the $GETXPATH and $GETSPATH variables when it returns a positive answer. This helps you determine what path was taken to the occurrences found.
To show some sample uses of $GETVMATCH, we'll assume the following record is referenced:
STUDENT.NUMBER = 226823846; QYY = 195; COURSE = Etiquette 101: Introduction to Introductions; COURSE = Art 115: Late 19th-Century Dutch: The Gogh-Gogh Years; COURSE = Lit 203: 3 American Writers: Poe White Nash; QYY = 295; COURSE = Music 103: Bach's Scores, Low Pitches, Baroquen Plays; COURSE = Lit 205: Tennyson, Anyone? -> show eval $getvmatch(course,0,'Lit?',any) 2 -> show eval $getcval(course,2,'None',any) Lit 203: 3 American Writers: Poe White Nash -> show eval $getvmatch(course,3,'Lit?',any) 4 -> show eval $getvmatch(course,0,'Music?') -1
The reason the last command failed is because without the ANY option (or some other value for the structural-occurrence map), SPIRES looks only at the first occurrence of QYY, so it doesn't get into the 295 structure. In that command, if you add ANY to the function, SPIRES would return the value "3". If you add "1" to the function as the structural-occurrence map, SPIRES would return the value "0", since the Music 103 course is the first (i.e., number 0) occurrence in the requested structure.
--------------------------------------------- Functions: $GETVOCC Purpose: Retrieve the number of element occurrences from a referenced record ---------------------------------------------
The $GETVOCC function provides a way to access the number of occurrences of data elements within records. This function works similarly to the $GETxVAL functions: the argument list is similar, and the method of accessing the data is the same.
$GETVOCC has the following argument list:
$GETVOCC(elem-name, structural-occ-map, filter-indicator)
where "elem-name" is the name of the element to access, "structural-occ-map" is used in the same manner as for the $GETxVAL functions, and "filter-indicator" tells whether or not filtering should be used during the access. FILTERED is the default condition and can be omitted. To turn filtering off, use UNFILTERED.
The function returns an integer value as follows:
- a positive integer = the number of occurrences of the element within its subtree. NOTE: The value returned is the number of occurrences of the element within its enclosing structure. This is consistent with the $GETxVAL functions.
- 0 (zero) = there are no occurrences.
- -1 (negative 1) = an error occurred.
The $GETVOCC function sets the $GETXPATH and $GETSPATH variables when it returns a positive answer. This helps you determine what path was taken to the occurrences found.
------------------------------------------------ Function: $HEX Purpose: Convert value to type HEX Related function: $RETYPE ------------------------------------------------
The $HEX function has the following form:
$HEX(argument)
Executing the function converts the "argument" to type hex, unless the form of the argument is incompatible with hex, in which case a conversion error would occur.
--------------------------------------------- Function: $INDEXINFO Purpose: Access index information from a file definition Related functions: $ELEMINFO, $ELIDINFO ---------------------------------------------
The $INDEXINFO function returns a value from an occurrence of an index info-element.
The syntax of the function is:
$INDEXINFO(searchterm,info-element,occurrence,default)
"Searchterm" is any valid index name available to the user (i.e., indexes hidden by NOSEARCH are not known to this function.) [In case of duplicate searchterms, the first term encountered will be the one used; there is no way to use structural path information to qualify a searchterm for greater precision.] If the index named is not defined, the function returns a null value.
"Info-element" is the name or alias of one of the index info-elements. It designates what information is to be retrieved from the index info-elements. Possible values are:
NOTE TRUNCATE DESCRIPTION, DESC EXCLUDE, EXC SOURCE, SOU USERINFO VALUE-TYPE, VTYPE
If the info-element is invalid, a null value is returned.
"Occurrence" is an optional parameter. It is a value, starting from 1, that says what occurrence of the specified info-element is to be accessed. If not given, the first occurrence is assumed. The only index info-elements that are multiply-occurring are DESCRIPTION, SOURCE, EXCLUDE, and USERINFO.
The "default" parameter is also optional, and lets you specify a value to be returned if the requested occurrence of the info-element does not occur.
--------------------------------------------- Function: $INDEXNUM Purpose: Retrieve index number from a search term for use in $INDEXTERM function Related functions: $INDEXTERM ---------------------------------------------
The $INDEXNUM function returns an integer (0 or greater) representing the position of the given searchterm in the list of indexes currently available to the user.
The syntax of the function is:
$INDEXNUM(searchterm)
"Searchterm" is any valid simple-index, goal-index or qualifier name available to the user (i.e., indexes hidden by NOSEARCH are not known to this function.) [In case of duplicate searchterms, the first term encountered will be the one used; there is no way to use structural path information to qualify a searchterm for greater precision.]
If the supplied name is not a valid searchterm, or if no subfile is selected, the function returns "0". Otherwise, it returns the ordinal position of the named index in the list of indexes (SHOW INDEXES), counting any qualifiers, and counting compound indexes as one. The "Goal Records" or "Goal -- Index", the first item in the SHOW INDEXES list, is always number 1 in the count.
The most common use of this number is with the $INDEXTERM function, which provides specific information about an index, given its number.
Here is an example session (the SHOW INDEXES display is condensed):
-> select restaurants Goal Records: RESTAURANT Compound Index Terms: CREDIT BAR Simple Index: N, NAM, NAME Simple Index: C, CUIS, CUISINE, T, TYPE, Simple Index: A, ADD, ADDRESS, ST, STREET -> show eval $indexnum(restaurant) 1 -> show eval $indexnum(address) 5 -> show eval $indexnum(credit) 0
"Credit" returns a zero because it is not a simple index. Compound indexes are internally handled very differently from simple indexes, and the function does not have access to the external searchterms of compound indexes.
--------------------------------------------- Function: $INDEXTERM Purpose: Retrieve information about an index Related functions: $INDEXNUM ---------------------------------------------
The $INDEXTERM function returns information about a given index, sub-index or qualifier, such as its valid searchterms.
The syntax of the function is:
$INDEXTERM(index-number,code)
The index-number is an integer representing the index's ordinal position in the list of indexes displayed to the user by the SHOW INDEXES command. The $INDEXNUM function can be useful for retrieving that number for a given index name.
The allowed codes are:
If the supplied number is not valid for an index, or if no subfile is selected, the function returns a null string. [See the description of $INDEXNUM to see how to count the indexes correctly.]
Here is an example session (the SHOW INDEXES display is condensed):
-> select restaurants Goal Records: RESTAURANT Compound Index Terms: CREDIT BAR Simple Index: N, NAM, NAME Simple Index: C, CUIS, CUISINE, T, TYPE, Simple Index: A, ADD, ADDRESS, ST, STREET -> show eval $indexterm(5,name) A, ADD, ADDRESS, ST, STREET -> show eval $indexterm(4,immediate) NOT IMMEDIATE -> show eval $indexterm(2,type) COMPOUND -> show eval $indexterm(2,name) ID
For the compound index, the NAME code makes the function return the internal name coded for the searchterm in the file definition -- it is ID for the compound index in RESTAURANT. It is not a useful value to users. There is no way for the $INDEXTERM function to retrieve the actual searchterms used with a compound index.
---------------------------------------------- Function: $INSERT Purpose: Insert one string into another string ----------------------------------------------
The $INSERT function lets you insert one string into another.
The function has the following form:
$INSERT(s1,s2,pos)
s1 and s2 are string parms, and pos is an integer parm.
The input string, s1, represents the value that you wish to have adjusted, while s2 specifies the insertion string, and pos specifies the position within s1 that you wish to insert s2, counting from the left.
This function inserts s2 into s1 after pos characters, counting from the left of s1. pos may range from 0 thru the size of s1. If pos equals zero, the result is the same as the concatenation: s2||s1. If pos equals the size of s1, the result is the same as: s1||s2. If pos is negative or greater than the size of s1, then s1 is returned unchanged. Likewise, if either s1 or s2 are null strings, s1 is returned.
To insert s2 from the right, simply set pos to the expression "size of s1" minus "right position".
Note that the $INSERT function parallels the $INSERT system proc, with LEFT or FRONT as the "how".
Here is an example of the use of this function:
Protocol: Values Returned: __________________________ ________________
let s1 = 'Happy' MealHappy let s2 = 'Meal' HMealappy let n = $INT(0) HaMealppy while #n <= $size(#s1) HapMealpy show eval $insert(#s1,#s2,#n) HappMealy let n = #n + 1 HappyMeal endw return
---------------------------------------------- Functions: $INSETL, $INSETR, $INSETC Purpose: Layout a string value in a field ----------------------------------------------
The $INSETL, $INSETR, and $INSETC functions let you layout a value more distinctively by centering the value ($INSETC) or adjusting it to the left ($INSETL) or right ($INSETR) in a specified field, meanwhile surrounding the value with a repeating character such as a period.
The functions have the following form:
$INSETL(input-string,background-string,integer3) $INSETR(input-string,background-string,integer3) $INSETC(input-string,background-string,integer3)
The input string in each case represents the value that you wish to have adjusted, while "background-string" specifies the repeating character, and integer3 specifies the total space of the field. (The field can be 256 characters maximum.)
For $INSETL, the input string is left justified in a field integer3 characters wide. If the input string is less than integer3 characters long, then the background-string is used repeatedly to pad the field on the right.
For $INSETR, the input string is right justified in a field integer3 characters wide. If the input string is less than integer3 characters long, then the background-string is used repeatedly to pad the field on the left.
For $INSETC, the value is centered as closely as possible within the field. It is padded on either side with the character specified as the background-string.
If the value is longer than the field, it is truncated on the right for $INSETL and $INSETC, and truncated on the left for $INSETR.
Note that the $INSETL and $INSETR functions parallel the $INSETL and $INSETR system procs, but the procs do not truncate long values.
Here are some examples of uses of these functions: ($key is 09/11/81)
Function: Value Returned: __________________________ _______________ $INSETL(ID,.,15) ID............. $INSETL(ID,.,5)||$INSETR($key,.,10) ID.....09/11/81 $INSETR($key,.,15) .......09/11/81 $INSETR($key,.,5) 11/81 $INSETC(Address,'-',15) ----Address----
------------------------------------------------ Function: $INTEGER ($INT) Purpose: Returns a value converted to type INTEGER Related function: $NEGATIVE ------------------------------------------------
The function $INTEGER (alias $INT) converts its argument into a four-byte integer. The form of the function is as follows:
$INTEGER(input-value)
"Input-value" must be either an integer or a value convertible to an integer, or a conversion error will occur. [See 5.1.2.] (The argument can range from -2,147,483,647 to 2,147,483,647.)
A null value will convert to a zero, type INT.
Among other possibilities, you can use $INT to ensure that the value you assign to a dynamic variable is of type INTeger:
let Integer = $int(4)
If there is a chance that the value is not an integer -- for instance, if it might contain decimal places -- you should probably use $PACKED or $REAL instead. For values falling outside the range specified above for integer, use $PACKED.
---------------------------------------------- Function: $ISSUECMD ($ISSUE) Purpose: Issue a non-SPIRES command (e.g., from a format or USERPROC) ----------------------------------------------
The $ISSUECMD function, alias $ISSUE, sends a command to the underlying system, and returns an integer of 1 if successful, or 0 if the command fails. The function has the following form:
$ISSUECMD(command)
The "command" to be issued may not be a SPIRES command.
Because you can issue subsystem commands from a protocol without using the $ISSUECMD function, the function will be primarily useful in formats or in USERPROCs in file definitions.
Some examples of $ISSUECMD in action:
LET N = $ISSUECMD('date') EVAL $ISSUE('save ' || #DATASET)
---------------------------------------------- Function: $ISSUEMSG Purpose: Issue a system message ----------------------------------------------
The $ISSUEMSG function issues a SPIRES system message of your design. You can specify its type as Informational, Warning, Serious or Unconditional. Unconditional messages are sent to the currently assigned MSG area (usually the terminal) regardless of the error level set by any SET MESSAGES commands. The other types are controlled by I, W, and S error levels for interactive or XEQ mode, depending on which mode executed the command leading to the $ISSUEMSG function.
Here is the syntax of the $ISSUEMSG function:
$ISSUEMSG(message,code)
where "message" is a string value containing the message to be issued, and "code" is one of the following:
Note that other abbreviations are allowed for the code: any abbreviation down to one character, in fact.
The function normally returns the message string as its value. However, if the string is null, a one-byte blank-character string is returned. If no code or an invalid code is specified, the function returns a null value.
When the message is displayed, it will be preceded by a hyphen, as system messages normally are.
Here are some examples, issued with the EVAL command from interactive mode. Assume SET MESSAGES = 3 is in effect:
-> eval $issuemsg('Illegal input; please try again', Warning) -193(W)- Illegal input; please try again -> show eval $mnum 193 -> eval $issuemsg('Danger! Danger!', Unconditional) -Danger! Danger! ->
---------------------------------------------- Function: $LEFTSTR ($LSTR) Purpose: Create substring from left portion of a string value Some related functions: $LEFTSUB, $RIGHTSTR, $RIGHTSUB, $SUBSTR, $XSTR ----------------------------------------------
The $LEFTSTR or $LSTR function truncates an input string, returning a substring consisting of a certain number of characters from the leftmost portion of the input string. The function takes the form:
$LEFTSTR(input-string,integer)
The "integer" argument determines how many characters of the input string will be returned as a substring.
For example:
-> let x = 'ABC$DEF' -> let y = $LEFTSTR(#x, 3) -> show eval #y ABC
------------------------------------------------- Function: $LEFTSUB ($LSUB) Purpose: Return a substring truncated to the left of a specified substring Related functions: $RIGHTSUB, $LEFTSTR $RIGHTSTR, $SUBSTR, $XSTR -------------------------------------------------
The $LEFTSUB or $LSUB function truncates a value to its leftmost or beginning portion.
The function has the form
$LEFTSUB(input-string,string2)
If string2 is a substring of the input string, then $LEFTSUB returns all of the input string up to (but not including) the first character of string2. [If argument 2 is null or not contained by argument 1, then the value returned is null.]
Here are a few examples of $LEFTSUB:
-> show eval $acct GQ.ASH -> show eval $lsub($acct,.) GQ
Another example:
LET HOUR = $LSUB($TIME,':') IF #HOUR < 17 THEN LET PERIOD = DAY
I.e., if the value for $TIME were 16:32:04, then HOUR would be 16 and PERIOD would be DAY.
-------------------------------------- Function: $LINE Purpose: Convert value to type LINE --------------------------------------
$LINE is a single-argument function that converts its input value to type LINE. It is a single argument function of the form:
$LINE(input-value)
$LINE can be used to help ensure that a value being compared to a line variable such as $WDST represents a valid value for a line.
------------------------------------------ Function: $LOG Purpose: Compute logarithm of value Related functions: $EXP, $MOD, $RANDOM, $SQRT, $TRUNC ------------------------------------------
$LOG is a single argument function of the form:
$LOG(real-value)
"Real-value" is any value that either is of type REAL (i.e., floating-point) or that can be successfully converted to type REAL. $LOG returns the Naperian logarithm (base "e") of its argument. (The returned value is also of type REAL.)
If the argument is zero or negative, $LOG returns a zero result.
Some examples follow:
LOG function Real Result --------------- ----------- $LOG(10) 2.30259 $LOG(1) 0 $LOG(0.5) -0.69315 $LOG("-3.1415") 0
---------------------------------------------- Functions: $LOOKSUBF, $LOOKSUBG Purpose: Access records from other subfiles or record-types ----------------------------------------------
The $LOOKSUBF and $LOOKSUBG functions let you access records from other subfiles or record-types. ($LOOKSUBF is most commonly used when you have another subfile selected, but in fact, it can be used whether or not a subfile is currently selected.) SPIRES will look in the specified subfile or record-type for a record having a key equal to the value you have specified in the function. At that point, depending on the other values you have specified in the function's argument-list, the function either verifies that such a record exists, or returns a count of the number of occurrences of an element, or returns the value of an element in that record. The element's value can be returned in either its internal or external form.
The syntax is:
$LOOKSUBF(value,how,subfile,element-name,via,default,occ) $LOOKSUBG(value,how,subgoal,element-name,via,default,occ)
The parameters can have the following values:
- VERIFY will look for a record with the specified key. If a record is found, the function will return a 1; if not, the function will return a 0.
- NONE.VERIFY will verify that no records exist with the specified key. If a record is not found, the function will return a 1; if a record is found, it will return a 0.
- REPLACE will access a record with the specified key and return the internal form of the element specified in the element-value parameter of the function. If no record is accessed, and no default parameter is specified, the function returns a null value.
- EXTERNAL is similar to REPLACE except that it returns the external form of the element.
- COUNT is used to count the number of occurrences of an element.
- LCTR -- the "value" given in the first parm is a locator and the subfile or subgoal is usually an index or REMOVED record-type.
- NODEFQ -- the deferred queue should not be examined for records; the "value" given in the first parm is the record key in its internal form.
- NODEFQ.LCTR -- a combination of the two previous values.
- EXTKEY -- the "value" given in the first parm is the record key in its external form.
- NODEFQ.EXTKEY -- the same as EXTKEY, and the deferred queue should not be examined for records.
- TRANSACTIONS -- In this special case, for retrieving transaction elements from the $TRANSACTIONS record-type, the first parm's value is ignored; SPIRES knows what $TRANSACTIONS record to get the data from. This is only available under FOR TRANSACTIONS processing, and is only useful inside a format. The "subgoal" for $LOOKSUBG would be the name of the pseudo-record-type that is defined by the $TRANSACTIONS record-type; if that record-type is defined as the goal record for a subfile, you can name that subfile in $LOOKSUBF. For more information about using $TRANSACTIONS, see the SPIRES manual "File Management"; online, EXPLAIN $TRANSACTIONS RECORD-TYPE. You can not COUNT with TRANSACTIONS.
Below are some examples:
-> show eval $looksubf($int(123),verify,orders)
SPIRES will look in the subfile named ORDERS for a record with the integer key "123". If a record is found, the function will return a 1; if no record is found, it will return a 0.
-> show eval $looksubf($int(123),replace,orders,desc,,none)
SPIRES will look for record 123 in the subfile ORDERS and return the value of the element DESC. If no record is accessed, or no value exists for DESC, the function will return "none".
-> show eval $looksubg($int(123),verify,rec04)
SPIRES will look in the record-type named REC04, which must be defined as a subgoal for the currently selected subfile, and verify the existence of a record with the integer key "123".
-> show eval $looksubg(PHYSICS,count,indx7,ptrg)
SPIRES will look in the record-type named INDX7, which must be defined as a subgoal for the currently selected subfile, and count the occurrences of the PTRG element in the record with the "PHYSICS" key. If this is a LARGE SEGMENT record, the entire large segment chain will be traversed to return the total count of PTRG elements across all segments.
If you include REPLACE in the function to access the internal form of the element, and that element has OUTPROC-REQ coded for it in the file definition, the function will return a value as if no value for the element existed. (i.e., a null or the value coded in the DEFAULT parameter).
---------------------------------------------- Functions: $LOOKSYS Purpose: Retrieve record data from system subfiles Related function: $LOOKSUBF ----------------------------------------------
This function, similar to $LOOKSUBF, provides access to records in several system subfiles. You provide: a code identifying the system subfile you want to use; the key of the record you're interested in; and the name of the element whose value you want returned for the record you named.
Currently it is useful only in Prism for use with Prism meta-data.
Its syntax is:
$LOOKSYS(filecode,record-key,elem-name,occ)
ROUTE - information about Prism electronic forms routing UNIVID - information about people with university ids PERSON - information about people in directory.
The record-key will always be converted to STRING and passed through the INPROC rule for the key of the target subfile before retrieval takes place. A null value for the record-key is not allowed, and causes an error. The returned value, if it exists, will always be in its external form. If it doesn't exist, a null value will be returned by the function.
$LOOKSYS translates to $LOOKSUBF in the following way:
$LOOKSYS(filecode,record-key,elem-name,occ) $LOOKSUBF(record-key,EXTERNAL,!LOOKSYS_filecode,elem-name, EXTKEY,,occ)
--------------------------------------------------- Function: $MATCH Purpose: Compare a value against a predetermined list of string patterns Related functions: $PMATCH, $AMATCH, $APMATCH ---------------------------------------------------
The $MATCH function matches an input string against an argument list of string patterns. The form of the function is as follows:
$MATCH(input-string,pattern1,pattern2,....)
The first argument names an input string (explicitly or by naming a variable) against which one or more patterns are to be matched. The patterns are given in the second and succeeding arguments. [The number of arguments allowed in the $MATCH function can vary -- you can count on roughly twenty patterns being allowed. If you need a larger number than this, you might consider storing the patterns in an array and using $AMATCH or $APMATCH.]
The patterns are strings, which may begin and/or end with a '?'. If a pattern begins with a '?', then anything may precede the pattern string in the input string:
?XYZ will match the strings AXYZ and XYZ, but not AXYZA.
If a pattern ends with a '?', then anything may follow the pattern string in the input string:
XYZ? will match the strings XYZAB and XYZ, but not AXYZ.
If a pattern both begins and ends with a '?', then anything may precede or follow the pattern string in the input string:
?XYZ? will match AXYZ, XYZ, XYZA, but not XZ or ZYX.
If a pattern has a '?' embedded in it, the '?' matches any or no characters in the input string:
?T?T?A? will match I THINK THEREFORE I AM and TRY THE APPLE.
Any character string containing embedded blanks must be enclosed in quotes.
The $MATCH function returns the integer number of the first pattern string in which a match succeeded. If no match is found, 0 is returned.
Examples: LET M = $MATCH(Y, X, Y, Z) sets M to 2 LET M = $MATCH (Y, XYZ) sets M to 0 LET M = $MATCH (XYZ, ?Y?) sets M to 1 LET M = $MATCH (XY, ?Y) sets M to 1 LET M = $MATCH (XYZ, ?Y) sets M to 0 LET M = $MATCH ($ASK, Y?, N?) sets M to 1 for YES, 2 for NO and 0 for yes or no.
As shown by the last example, case is significant in the specification of $MATCH arguments. If the input string, argument 1, is $ASK, it may be helpful to specify the case of the ASK command locally, using ASK UPPER. [See 5.3.] You can also use the $CAPITALIZE or $CASE function. For example:
LET M = $MATCH($CAPITALIZE($ASK),Y?,N?) sets M to 1 for YES or yes and sets M to 2 for NO or no.
----------------------------------------------------- Function: $MOD Purpose: Find remainder of division between integers -----------------------------------------------------
$MOD is a two-argument function in this form:
$MOD(integer1,integer2)
where both arguments must either be of type integer or be convertible to type integer. $MOD returns the remainder from the division of the first argument by the second. If the second argument is "0", then "0" is returned.
For example,
$MOD(5,4) = 1
If both arguments have the same sign (positive or negative), then the result is simply the remainder from the integer division. However, if they have different signs, then the second argument, the divisor, is added to the remainder from the integer division.
For example,
$MOD(9,4) = 1 (remainder = 1) $MOD("-9","-4") = -1 (remainder = -1) $MOD("-9",4) = 3 (remainder = -1; -1 + 4 = 3) $MOD(9,"-4") = -3 (remainder = 1; 1 + (-4) = -3)
------------------------------------------------ Function: $NEGATIVE ($NEG) Purpose: Convert value to type INTEGER and change the value's sign Related function: $INTEGER ------------------------------------------------
$NEGATIVE (or $NEG) is a one-argument function of the following form:
$NEGATIVE(input-value)
where "input-value" must be an integer or convertible to an integer.
$NEGATIVE changes the sign of its value, from positive to negative or vice-versa:
-> let x = $negative(3) -> let y = $negative(-4) -> show eval #x ' ' $type(#x) -3 INT -> show eval #y ' ' $type(#y) 4 INT
The input value for $NEGATIVE (as for $INTEGER) must be within -2,147,483,647 and 2,147,483,647 or a conversion error will occur.
For arithmetic with decimal values, use $REAL or $PACKED instead.
---------------------------------------------- Function: $NOLF Purpose: Write a string to the terminal, suppressing the final line feed and carriage return ----------------------------------------------
The $NOLF function lets you write a line to the terminal without a carriage-return and line feed being appended to it. The next line written to the terminal will be on the same line as the last.
The function has the form:
$NOLF('input-string')
where "input-string" is the string (or message) to be written to the terminal. The function is nearly always used with the EVAL command, e.g., EVAL $NOLF('input-string'). If you specify the null string (i.e, ''), the terminal will produce an "idle" character (the print head or cursor will jump, then return to its starting location).
------------------------------------------------ Function: $NORMALIZE function Purpose: Returns a normalized packed decimal value as a string ------------------------------------------------
The form of the function is as follows:
$NORMALIZE(packed-value)
$NORMALIZE takes a packed-value, or any value that converts to packed, and returns a normalized string suitable for $PACK. A "normalized" value has only three basic forms:
1. Pure integer, like: 2315 2. Exponential, like: 1.234E5 or 8.712E_9 or 8E29 3. Mixed number, like: 7.128 (one digit before decimal point)
Here are examples of each form:
1. $normalize('12.334E3') -> 12334 2. $normalize('12.334E_2') -> 1.2334E_1 3. $normalize('12.334E_1') -> 1.2334
If you intend to do numeric comparisons of the result, you should probably apply $PACK to the result.
----------------------------------------- Function: $PACKED ($PACK) Purpose: Return a value converted to type PACK -----------------------------------------
The $PACKED (or $PACK) function converts the argument to a packed decimal value. The function's form is as follows:
$PACKED(input-value)
"Input-value" is any value that can be successfully converted to a packed data type. (Numeric values such as integers can successfully convert to packed, though the reverse is not necessarily true.)
A packed decimal allows SPIRES to handle very large numbers properly, whether they have decimal places or not. SPIRES can perform arithmetic operations with packed decimal values having up to 29 places of precision (the number of digits beginning with the left-most non-zero digit through to the right-most digit).
Exponential notation allows the value to be multiplied by as much as 10 to the 127th power, or divided by as much as 10 to the 128th power. The converted value will have such packed decimal characteristics as precision and magnitude, which are used in some of the other functions relating to packed decimals.
Input values outside of the allowed ranges return "@@" (positive infinity), "0" (zero) or "-@@" (negative infinity). A null input value will be converted to a zero.
Packed decimal value handling is described in detail in chapter 18 of the manual "SPIRES Technical Notes". Online, issue the command EXPLAIN PACKED DECIMALS for more information.
--------------------------------------------- Function: $PACKTEST Purpose: Query a packed decimal value Related functions: $DECIMAL, $PRECISION ---------------------------------------------
The $PACKTEST function returns information about a packed decimal value. Its syntax is:
$PACKTEST(value, code)
where "value" is a packed decimal value (or a value that can be converted to packed) and "code" is one of the following:
code meaning ----------- --------------------------------------- ZERO return the number of trailing zeroes PRECISION return the number of significant digits EXPONENT return the exponent value MAGNITUDE return the magnitude of the value DECIMAL return the number of decimal places
(The codes may be abbreviated to their first character.) The result is always an integer value. [EXPLAIN PACKED DECIMALS, PRECISION.] for information about these concepts in relation to packed decimal values, or see the manual "SPIRES Technical Notes".
Here are some examples of the $PACKTEST function:
$PACKTEST(13.450,ZEROES) = 1 $PACKTEST(13.450,PRECISION) = 5 $PACKTEST(13.450,EXPONENT) = -3 $PACKTEST(13.450,MAGNITUDE) = 2 $PACKTEST(13.450,DECIMAL) = 3
As demonstrated by the above example, the value returned by DECIMAL is the negative of the value returned by EXPONENT.
---------------------------------------------- Function: $PARSE Purpose: Break input value at a point to the left of a character in a second string Related functions: $BREAK, $SPAN, $STRIP, $PARSESTRIP Function: $PARSESTRIP Purpose: Remove container characters and "undouble" characters in $PARSEd value Related functions: $PARSE, $DOUBLE ----------------------------------------------
$PARSE is a three-argument function somewhat similar to (though more complicated than) $BREAK. The function's form is as follows:
$PARSE(input-string,string2,string3)
The arguments in the argument list are all required:
$PARSE searches the input string until it finds there one of the characters in string2 that is NOT in a "containing group" (i.e., NOT surrounded by one of the pairs of characters named in "string3"). When $PARSE finds such a character it stops scanning the input string any further and returns the characters of the input string, up to but not including the character that stopped the scan. (If no character stops the scan, all of the input string is returned.)
$PARSE is thus different from $BREAK in that it prevents a break character within a containing group from causing a break; $BREAK does not recognize containing groups.
$PARSESTRIP is used to strip off the container character at the front and back of a $PARSEd value (if any) and, if the input value has container characters that are doubled inside the front and back container characters, those doubled characters will be undoubled. So, for instance, if $PARSE returns:
"a streetcar named ""Dewayne"""
where the quotation marks are container characters, then $PARSESTRIP applied to it would return:
a streetcar named "Dewayne"
The syntax of $PARSESTRIP is:
$PARSESTRIP(input-string,string3)
where "input-string" is the string value returned from $PARSE, and "string3" is the same string defined as "string3" in $PARSE, that is, the characters that begin and end containing groups.
As stated above, the paired characters in "string3" determine what comprises a containing group. If both characters in a pair are the same (e.g., "%%"), they define a "delimiter group". If they are different (e.g., "{}") they define a "nesting group". Delimiter groups may not be nested.
If string3 has an odd number of characters, the last (odd) character is ignored. Any character used in one pair cannot be used in another pair. If a character is used again in a second or subsequent containment pair, or if a character in string2 is used in a containment pair, that pair is ignored. By default, if string3 is null, apostrophes and quotation marks are used to define delimiter groups (though their appearance in string2 would override the default). String3 must be included in the function, so to be "null", it must be specified as a pair of apostrophes or quotation marks.
Suppose you are picking off the individual tokens of an options list:
TITLE 'John''s title' FORMS BOND
You set up a loop that first parses the value at blanks, unless the blanks are within allowed containers, which in this case are apostrophes or quotation marks:
$PARSE(#OptionList,' ','')
$PARSE is thus looking for the first blank in the value that is not within a containing group, which is defined by default as being surrounded by either apostrophes or quotation marks. Hence, TITLE would be returned as the value of the function, as the first token.
Your loop would then strip off the first token (e.g., using $RSUB), and start again. The second time:
$PARSE(#OptionList,' ','') = 'John''s title'
Note that the apostrophes, the container characters, are part of the returned value. If you did not want them, or if the contained value itself includes a container character (doubled), then $PARSESTRIP would come in handy:
$PARSESTRIP($PARSE(#OptionList,' ',''),'') = John's title
$PARSE sets the system variable $SNUM to 0 if the scan does not encounter errors. Otherwise, $SNUM is set to one of the following error numbers:
-> let x = '(3+5)) + (2*10)' <--note unbalanced parentheses -> let parsed = $PARSE(#x,',','""''''()') -> show eval #parsed (3+5) -> show eval $snum 772
(The delimiters for string3 are hard to read in this example, but consist of a pair of quotation marks, a pair of apostrophes, and a set of parentheses.)
------------------------------------------------- Functions: $PATHFIND, $PATHINFO Purpose: Obtain information about a path or about the subfile (format, etc.) open on that path -------------------------------------------------
The two functions $PATHFIND and $PATHINFO retrieve information about a path that has been opened as part of your application's processing -- or about the subfile that is selected (or the format or vgroup that is set) on that path. [For more information about path processing in general, see the manual "SPIRES Technical Notes".]
The $PATHFIND function returns the integer path number of a path that has been specified by path-name or subfile-name. The function's form is as follows:
$PATHFIND(path-string,code)
"Path-string" is either the name of the opened path or else the name of the subfile selected on that path. If it is longer than one word, the subfile name should be placed in quotes; it may also be fully qualified (EXPLAIN SUBFILE NAMES, NON-UNIQUE for details) or it may be a temporary subfile name.
The value for "code" tells SPIRES what your "path-string" value represents:
Code Purpose ------- ------------------------------------------------------ SUBFILE tells SPIRES that the "path-string" is the name of a subfile. PATH tells SPIRES that the "path-string" is the name of the path itself.
(These values for "code" can be abbreviated to three letters.)
If $PATHFIND does not find a path corresponding to the input values you specify, the function returns a value of -1. Otherwise, it returns a value of 0 for the primary path, and 1 through n for other paths.
For example:
-> select bookstore -> thru path1 select formats -Path established: 1 PATH1 -> thru path2 select filedef -Path established: 2 PATH2 -> show eval $pathfind('bookstore',SUBFILE) 0 -> show eval $pathfind('filedef',SUBFILE) 2 -> show eval $pathfind('path1',PATH) 1
The $PATHINFO function returns a string representing information for a path that you have specified by path-number. The function's form is as follows:
$PATHINFO(path-number,code)
"Path-number" is the number of the path for which you want information, counting from 0 for the primary path.
"Code" currently has these possible values, which can all be abbreviated to three letters:
Code Purpose ------- -------------------------------------------------------- NAME returns the path's name, if there is one. (Otherwise it returns a null string.) TYPE returns the path's type: possible values are ATTACH, SUBFILE, FORMAT, or VGROUP. SUBFILE returns the name of the subfile, if any, that was selected on the path.
For example:
-> select bookstore -> thru formpath set format bookread -Path Established: 1 FORMPATH -> show eval $pathinfo(1,TYPE) FORMAT -> show eval $pathinfo(1,NAME) FORMPATH -> thru newpath clear vgroups -Path established: 2 NEWPATH -> show eval $pathinfo(2,TYPE) VGROUP
$PATHINFO returns a null string if the requested value is null or if the information you've requested simply doesn't exist for that path.
Although the ATTACH command can't be issued on a path, you might use $PATHINFO to determine whether the primary path represents an attached record-type or a subfile select.
-> attach 2 of gq.abc.books -> show eval $pathinfo(0,TYPE) ATTACH
--------------------------------------------------- Function: $PMATCH Purpose: Match a value against a pattern or stem Related functions: $AMATCH, $APMATCH, $MATCH ---------------------------------------------------
The $PMATCH function matches an input string against one or more string patterns, returning a number corresponding to the number of the pattern that matched the input string. $PMATCH operates similarly to the $MATCH function, but is more useful and efficient than $MATCH for matching by an exact stem, as demonstrated below.
The form of $PMATCH is as follows:
$PMATCH(input-string,pattern1,pattern2,...)
where:
$PMATCH attemps to match its input string against each of the patterns and returns an integer "n" corresponding to the "nth" pattern, the one that has satisfied the match requirements: [The matching of the input string to the patterns is attempted through the length of the input string. Thus, if an input string of three characters matches the first three characters of a pattern, the function is satisfied; any additional characters that the pattern might have (e.g., a fourth or fifth character) would be disregarded. (A pattern shorter than the input string would be skipped altogether.)]
-> let status = FRESH -> show eval $PMATCH(#status,FRESH,SOPH,JUN,SEN,GRAD) 1
If $PMATCH cannot find a match, it returns a value of "0" (zero).
Case is significant for matching with $PMATCH:
(1) $PMATCH(Y, YES, OK, NO) = 1 (2) $PMATCH(yes, YES, OK, NO) = 0
In example (1) the input string "Y" matches "YES" through the length of the input string. ("YE" or "YES" as input strings would also return "1" but a input string of "YESIREE" or "YEP" would return "0".) In example (2), however, the lower case input string "yes" does not match the upper case pattern string "YES", so $PMATCH returns a "0" value. The $CASE or $CAPITALIZE function can alleviate this problem:
$PMATCH($CAP(yes), YES, NO) = 1
If you want to ensure that no match is made unless the input string matches a certain minimum number of characters in a pattern, the pattern should have a question mark following the minimum desired length.
The following example enforces a minimum length of three characters for the stem:
$PMATCH(#x,TRA?NSFER,DIS?PLAY)
If #X = "D" or "DI", no match is made ("0" is returned), since the "minimum" value of #X for a match is "DIS" or "TRA". If #X = "DIS" or "DISP" or "DISPLA", the function would return the integer "2". (Don't forget to capitalize the value before matching it with upper case patterns.)
--------------------------------------------- Functions: $PRECISION and $WINDOW Purpose: Change precision of a packed decimal value Related functions: $DECIMAL, $PACKTEST ---------------------------------------------
The $PRECISION and $WINDOW functions are used to change the "precision" of a numeric value, where precision is defined as the number of digits in the value counting from the left-most non-zero digit to the right-most digit.
The syntax is:
$PRECISION(value,precision) $WINDOW(value,precision)
where:
The $PRECISION function can add or subtract precision; the $WINDOW function will only subtract precision, but is otherwise the same as $PRECISION.
Here are some sample values as handled by $PRECISION:
Input value Precision Result ------------- ----------- ------- (1) 3 4 3.000 (2) 123.45 4 123.5 (3) 0.012345 4 0.01235 (4) 12345 4 1234E1 (5) 9999.56 4 10000
In example (1), zeroes are appended to create four places of precision in the value. (The same value processed by $WINDOW would have been unchanged, since $WINDOW does not add precision.) In example (2), the input value has five places of precision, so appropriate rounding is applied, and the value is reduced to four places of precision. In example (3), note that the result has only four places of precision, since precision is counted from the left-most non-zero digit in the value.
In example (4), the result is displayed in exponential notation because the result "12340" would contain five places of precision, which is more than was requested. In example (5), the result actually has five places of precision, due to rounding. This unusual occurrence can only happen when rounding raises a value to a power of ten (e.g., 10, 10000, 0.01).
For more information about the precision of packed decimal values, see the manual "SPIRES Technical Notes" or online EXPLAIN PRECISION OF PACKED DECIMALS.
--------------------------------------------- Function: $PRISMINFO Purpose: Retrieve information about current Prism environment ---------------------------------------------
The $PRISMINFO function returns information to applications about the current Prism environment. It packages information formerly available only by defining new system variables or Prism variables.
Currently, the function returns three types of information: information about the current search, information about the current sort, and information about the current Prism version.
The syntax of the function is:
$PRISMINFO(SEARCH,search-parm[,EXPANDED|TYPED] [,command-num]) $PRISMINFO(SORT,sort-parm) $PRISMINFO(VERSION,SHORT|LONG)
Search-parm" can be one of the following:
-> show eval $prisminfo(search,count) 6
-> show eval $prisminfo(search,result) 46
-> show eval $prisminfo(search,source) STACK
-> show eval $prisminfo(search,command,,1) Find NAME > a -> show eval $prisminfo(search,command,,6) AND NOT ABBREV DE
If there is no current search, then all of the SEARCH variants of the function return null.
The third parm (EXPANDED|TYPED) is reserved for a planned extension to the function.
"Sort-parm" can be one of the following:
-> show eval $prisminfo(sort,count) 2
-> show eval $prisminfo(sort,elements) FORM.NAME FORM.ID
This indicates that, for the user's current SORT command in Prism, the SPIRES sequencing was on FORM.NAME and FORM.ID elements.
-> show eval $prisminfo(sort,fields) FORM NAME(A), FORM ID(A)
If there is no current sort specified by the user, then all of the SORT variants of the function return null.
The VERSION variant of the function returns information about the current version of Prism (and SPIRES, if the LONG parm is used).
-> show eval $prisminfo(version,short) P -> show eval $prisminfo(version,long) Prod Prism 94.02, linked 02/25/94 at 23:50 (Prod SPIRES, linked 94.02.25)
For SHORT, the standard values are:
P - Production Prism N - Preproduction Prism T - Test Prism D - Development Prism O - Old Prism X - the "X" version of Prism
------------------------------------------------ Function: $PROCSUBG Purpose: Process input value through inprocs or outprocs for element in selected subfile's record-types ------------------------------------------------
The $PROCSUBG function lets you run any value through the inprocs or outprocs of an element in any record-type (or subgoal) of your currently selected subfile. [For a fuller description of inprocs and outprocs, see the manual "SPIRES System Procs".]
The syntax is:
$PROCSUBG(value,record-type,element-name,direction)
The parameters, all of them required, can have the following values:
Here is an example of $PROCSUBG running a dollar-type amount through the inprocs for a dollar-type element in the selected subfile:
$PROCSUBG('$54.00', REC01, AMOUNT, IN)
The function looks in REC01 of the currently selected subfile for inprocs on the AMOUNT element and applies them to the value "$54.00".
------------------------------------------- Function: $RANDOM Purpose: Generate a random number -------------------------------------------
$RANDOM is a one-argument function that generates a random number. The form of the function is as follows:
$RANDOM(n)
where "n" is a positive integer. (If n is negative, the function uses its absolute value.)
The $RANDOM function returns an integer in the range 0 to n-1, as the following example suggests:
-> show eval $random(100) 25 -> show eval $random(100) 71 -> show eval $random(100) 12
The initial "seed" from which numbers are derived is an internal clock, but you can use the SET SEED command (described below) to insure that $RANDOM always generates the same sequence of random numbers. The $SEED variable contains the current seed value.
The SET SEED command can be used in cases where you want to generate the same sequence of random numbers more than once. The form of the command is
SET SEED n
where "n" is a positive integer or zero (0). The value of the $SEED variable now contains this number.
For example,
-> set seed 1000 -> let x = $random(10) -> let y = $random(10) -> show eval '#x is ' #x ', #y is ' #y #x is 4, #y is 7 ->
A given seed value would always generate these same random values.
------------------------------------------- Function: $REAL Purpose: Convert value to type REAL (i.e., floating point) Related functions: $INTEGER, $LINE, $PACKED -------------------------------------------
The $REAL function has the following form:
$REAL(argument)
When the function is executed, the argument is converted to a real number. If the conversion is impossible, a conversion error results.
Among other possibilities, $REAL can be used to guarantee that a dynamic variable is assigned a value of type REAL.
A null value for the argument will convert to a zero, type REAL.
----------------------------------------- Function: $RECINFO Purpose: Return various types of information about a record Related function: $RECTEST -----------------------------------------
$RECINFO is a two-argument function that can be used to determine the actual storage size in bytes of a record in the selected subfile (or accessed record-type). The function can also return the four-byte hex locator for the record if it is removed to the residual.
The function's form is as follows:
$RECINFO(record-key-value, SIZE) or $RECINFO(record-key-value, LOCATOR)
where
If the value returned by the function is a positive integer, that is the size of the record in bytes. If no record with that key is found, the returned value is zero. If an error occurs during the processing of this function, the returned value will be a negative integer.
The size is the same value that would be returned by the SHOW RECORD OVERHEAD command; however, unlike that command, restricted to users with See access to the entire file, this function is available to any user of the subfile.
SPIRES returns a 4-byte hex value representing the locator in the residual of the record. If the record doesn't exist, an integer value of 0 (zero) is returned. If the record exists but the record-type is not Removed to the residual, then a value of 2 is returned. If the record exists in the deferred queue and the residual locator has not yet been assigned, the returned value is 1 (one).
----------------------------------------- Function: $RECTEST Purpose: Test whether input value is key of existing record and, if so, return status of record Related function: $DEFQTEST -----------------------------------------
$RECTEST is a one-argument function that can be used to determine whether a record with a particular key exists in the currently selected subfile (or accessed record-type), and if so, whether the record has been added, updated or removed since the file was last processed.
The function's form is as follows:
$RECTEST(record-key-value)
where
If the value returned by the function is a positive integer, the record exists in the subfile. If the value is less than or equal to zero, the record does not exist or an error occurred during the processing. In particular, if $RECTEST
Note that, if necessary, the input value may be driven through the inproc rules for the key.
----------------------------------------- Function: $REF Purpose: assign a static variable to another static variable defined as a reference variable Related functions: $SETPARMS, $GETPARMS -----------------------------------------
$REF is a one-argument function that connects a static variable or array to a reference variable or array. In essence, that means that anytime the reference variable is used, the current value of the variable it references will be used. This is particularly useful with the $SETPARMS and $GETPARMS functions; variables can then be shared between a program and subroutines that don't even know the name of the variable in the main program.
The function's form is as follows:
$REF(static-variable-name[,READONLY])
where
If the READONLY parameter is included, then you cannot change the value of the reference variable; if it is omitted, you can, and that change will also be reflected in the variable it references. [See 4.5.2 for more information on $REF usage.]
--------------------------------------------- Function: $REMAINDER Purpose: Return remainder of packed decimal division ---------------------------------------------
The $REMAINDER function returns the remainder from the division operation of one packed decimal value by another. [See the manual "SPIRES Technical Notes" for information on packed decimals in SPIRES.]
The function's form is as follows:
$REMAINDER(packed-dividend,packed-divisor)
"Packed-dividend" is the number being divided and "packed-divisor" the number performing the division. Both of these input values must be packed decimal values or convertible to packed decimal values (e.g., integer or real). The value returned will also be a packed decimal value.
Note that the two input values are separated by a comma, not a division sign -- e.g., $REMAINDER(1/3) is invalid syntax.
The division is performed to the maximum precision possible. For example, $REMAINDER(70,4) returns zero, because 70 can be divided by 4 with no remainder, i.e., 1.75. However, $REMAINDER(1/3) would return the packed value 1E_27 (1 times 10 to the minus 27th power), because the remainder is still non-zero at that level of precision.
If you are looking for a function to return the remainder of integer division, see $MOD.
------------------------------------------------ Function: $RESINFO Purpose: Return information about searches as maintained by result history Related functions: $SEARCHINFO ------------------------------------------------
The $RESINFO function provides an easy way to retrieve information about searches the user has done when result history is turned on (using the SET RESULT HISTORY command).
The function's syntax is:
$RESINFO(result-history-number,code)
where:
- PREVIOUS (or PRE): If the search was part of an iterative search, this code returns the result history number of the previous part of the search that this search was a continuation of. For example, if the @1 search is FIND CITY PALO ALTO and the @2 search is AND CUISINE FRENCH, then $RESINFO(2,PREVIOUS) would return the number "1". If the search whose result-history number you give in the function is the first part of an iterative search, then the value returned is "0".
- RESULT (or RES): This code returns the number of records in the result of the search whose result-history number you give. It will be an integer.
- COMMAND (or COM): This code returns the command that created this entry. It will be a string value whose length does not exceed 256 characters.
For PREVIOUS and RESULT, the function returns a "-1" value if the requested result history item is invalid, doesn't exist or encounters errors. For COMMAND, the function returns a null string in those cases.
For example, if you have the following result history:
-Result History: range=100; last=2 @1 Result: 41 find city prefix m @2 Result: 38 FIND @1 and phone pre 9
you would get the following data returned from various $RESINFO function requests:
$RESINFO(1,PREVIOUS) = 0 $RESINFO(1,RESULT) = 41 $RESINFO(1,COMMMENT) = find city prefix m $RESINFO(2,PREVIOUS) = 1 $RESINFO(2,RESULT) = 38 $RESINFO(2,COMMENT) = and phone pre 9
------------------------------------------------ Function: $RETYPE Purpose: Return a new value based on the input value but with redefined data type Related functions: $TYPE, $TYPETEST ------------------------------------------------
The $RETYPE function offers a way to return the input value with a redefined data type. The function's form is as follows:
$RETYPE(input-value,type)
where:
The $RETYPE function returns the redefined value of the input value, without changing the input value itself.
For example:
-> let x = XYZ -> show eval #X ' redefined as hex returns ' $retype(#X,hex) XYZ redefined as hex returns E7E8E9
$RETYPE can be useful for causing a value to be treated as having a particular data type, when you do not wish actually to convert it to that data type and risk changing the meaning of the value. For a detailed description of how $RETYPE can be useful in Userprocs, EXPLAIN SET VALUE UPROC, TYPE CONVERSION online or see the description of the $PROCVALUE variable in the manual "SPIRES File Definition".
A similar feature for redefining values of static variables, the REDEFINES statement, was described earlier in the section on vgroup definitions. [See 4.1.1.]
------------------------------------------------- Function: $REVERSE Purpose: Reverse characters in input string front to back -------------------------------------------------
The $REVERSE function turns the input string around, so that the first character is last, and the last is first.
The $REVERSE function's syntax is:
$REVERSE(input-string)
where "input-string" is the string to reverse.
The function returns the reversed string.
Here are a couple examples of its use:
$REVERSE($Ask) If $ASK is... then $REVERSE($Ask) returns... NROWS -> SWORN abc,def -> fed,cba
The function will probably be most useful in cases where you need to work with pieces of a value from right to left, but the functions you might want to use, such as $BREAK or $PARSE, work on values from left to right. You could first reverse the value with $REVERSE, pick off the last (now first) piece with the other function, and then again reverse that piece to put it in its normal orientation.
---------------------------------------------- Function: $RIGHTSTR ($RSTR) Purpose: Create substring from right portion of a string value Related functions: $LEFTSTR, $LEFTSUB, $RIGHTSUB, $STRIP, $SUBSTR, $XSTR ----------------------------------------------
The two-argument $RIGHTSTR function, alias $RSTR, lets you truncate an input string by the number of characters you specify in the second argument of the function, returning the right-hand portion of the input string. For example, if the current value of $DATE is 10/01/87, $RIGHTSTR($DATE,6) truncates the first six characters and returns the two rightmost characters:
-> show eval $rightstr($date,6) 87
The function's form is as follows:
$RIGHTSTR(input-string,integer)
The "integer" argument determines the number of characters to be truncated from the input string, starting at the leftmost portion of the string.
Example: Suppose X contains ABC$DEF. After executing LET CONST=$RIGHTSTR(#X, 3) CONST will contain $DEF.
Note that $LEFTSTR and $RIGHTSTR are complementary. The $LEFTSTR function returns the first n characters and $RIGHTSTR returns the characters to the right of the nth character. Thus, using the example above, a concatenation of $LEFTSTR and $RIGHTSTR returns the original input string.
LET CONST = $LEFTSTR('ABC$DEF',3) || $RIGHTSTR('ABC$DEF',3) CONST will contain ABC$DEF.
---------------------------------------------- Function: $RIGHTSUB ($RSUB) Purpose: Create substring from right portion of a string value Related functions: $LEFTSTR, $LEFTSUB, $RIGHTSTR, $STRIP, $SUBSTR, $XSTR ----------------------------------------------
The $RIGHTSUB or $RSUB function returns a truncated-from-the-front version of the input string, returning everything to the right of a substring. The function takes the form:
$RIGHTSUB(input-string,string2)
The value returned will be all of "input-string" to the right of "string2". Scanning for string2 is done from left to right, and scanning stops with the first occurrence of "string2" found.
If string2 does not occur in the input string, then $RIGHTSUB will return a null value. If string2 is null, then $RIGHTSUB will return the input string.
Below are examples of $RSUB:
input string string2 result --------------- ------- --------- PRICE UPDATE ' ' UPDATE ABC,DEF,GHI ',' DEF,GHI
Both $LEFTSUB and $RIGHTSUB discard the located "string2" part of the input string (e.g., there is no blank space before UPDATE in the result shown above).
--------------------------------------------------- Function: $RMATCH Purpose: Compare a value against a predetermined list of string patterns Related functions: $MATCH, $PMATCH, $AMATCH, $APMATCH, $ARMATCH ---------------------------------------------------
Like the $MATCH function, the $RMATCH function matches an input string against an argument list of string patterns. The form of the function is as follows:
$RMATCH(input-pattern,string1,string2,....)
The first argument names an input pattern (explicitly or by naming a variable) against which one or more strings are to be matched. The strings are given in the second and succeeding arguments. [The number of arguments allowed in the $RMATCH function can vary -- you can count on roughly twenty strings being allowed. If you need a larger number than this, you might consider storing the patterns in an array and using $ARMATCH.]
$RMATCH is the "Reverse" of $MATCH, in that the input value is specified as a pattern that is matched against strings. This is sometimes easier to use than $MATCH and $PMATCH when you have an input value that is a string that is matched against patterns.
The pattern is a string, which may begin and/or end with a '?'. If a pattern begins with a '?', then anything may precede the pattern string in the matching string.
?XYZ will match the strings AXYZ and XYZ, but not AXYZA.
If a pattern ends with a '?', then anything may follow the pattern string in the input string:
XYZ? will match the strings XYZAB and XYZ, but not AXYZ.
If a pattern both begins and ends with a '?', then anything may precede or follow the pattern string in the input string:
?XYZ? will match AXYZ, XYZ, XYZA, but not XZ or ZYX.
If a pattern has a '?' embedded in it, the '?' matches any or no characters in the input string:
?T?T?A? will match I THINK THEREFORE I AM and TRY THE APPLE.
Any character string containing embedded blanks must be enclosed in quotes.
The $RMATCH function returns the integer number of the first pattern string in which a match succeeded. If no match is found, 0 is returned.
As with $MATCH, case is significant.
---------------------------------------------- Functions: $ROOT Purpose: Find the n-th root of a packed value ----------------------------------------------
String_result = $ROOT(packed-value, integer-root)
$ROOT takes a packed-type, or any value that converts to a packed, as the first parameter, and an unsigned integer greater than 1 as the second operand. The second operand is the "root" to be obtained for the first operand. For even roots, the first operand must NOT be negative. For odd roots, the first operand can be any packed-type value. If "root" is 2, you get the square-root. If root is 3, you get the cube-root, etc. The practical limit for "root" is about 100. The result returned is the string form of a normalized number or mixed-number (like nnnn.fffff). You can supply "infinity" as the first parameter to $ROOT without error, unless it is -@@ for an even root.
Here is an interesting example using $ROOT and $NORMALIZE: [See 7.5.8b.]
The volume of the earth contains 1.33E50 atoms. The value for "pi" can be given by the ratio: $pack(355) / 113. The formula for VOLUME of a sphere is given by: V = 4/3 pi R^3. The RADIUS is therefore: R = $ROOT( V * 3 / 4 / pi). The SURFACE AREA is related to both V and R by the formula: S = V * 3 / R. So to find the surface area of the earth in numbers of atoms, we compute:
let V = $pack('1.33E50') let PI = $pack(355) / 113 let S = $normalize( #V * 3 / $root( #V * 3 / 4 / #PI, 3) )
S = 1.260042607478637474E34
The number of IPV6 Internet Addresses is given by:
let IPV6 = $normalize( $exp( 2, 120) )
IPV6 = 1.32922799578491869525565033784E36
So every atom on the surface of the earth could be assigned an IPV6 address and we would still have enough left over for more than 100 other earths!
---------------------------------------------- Functions: $LSTRIP and $RSTRIP Purpose: Strip away leading/trailing strings Related functions: $STRIP ----------------------------------------------
String_result = $LSTRIP(Input_string, Sub_string) String_result = $RSTRIP(Input_string, Sub_string)
These functions try to remove the Sub_string from either the Left or Right end of the Input_string. The functions continue to do that until the Sub_string can't be removed. If the Sub_string is just a single character, then $LSTRIP acts like $STRIP for a single character strip. Examples:
-> Let sample = '---+---' -> show eval $lstrip(#sample, '--') -+--- -> show eval $rstrip(#sample, '--') ---+- -> show eval $rstrip(#sample, '-') ---+ -> Let sample = '-|-|+-|-|' -> show eval $lstrip(#sample, '-|') +-|-| -> show eval $rstrip(#sample, '-|') -|-|+ -> show eval $rstrip(#sample, '|') -|-|+-|-
The functions can reduce the result to null if the Input_string consists of a repeating pattern of the given Sub_string. Typically, $RSTRIP is used to strip one or more of a particular character from the Right end of the Input_string, such as strip trailing blanks: $RSTRIP(Input_string, ' ')
---------------------------------------- Function: $SEARCHINFO Purpose: Return search commands issued for the current search ----------------------------------------
The $SEARCHINFO function is a two-argument function that takes the form:
$SEARCHINFO(integer,code)
where "code" in fact has only one feasible value: COMMAND. (You can abbreviate COMMAND to COM.)
The $SEARCHINFO returns the previously-issued search command corresponding to the integer named in parentheses after the function. For example, $SEARCHINFO(2,command) would return the second search command you have issued in your current search, $SEARCHINFO(4,com) would return the fourth search command issued in your current search, and so on.
Using $SEARCHINFO, you can retrieve any legal search command you have issued within your selected subfile since your last FIND command. (Compare this with the SHOW SEARCH command.) Here is an example using the Restaurant subfile:
-> find cuisine french -Result: 51 RESTAURANTS -> and (city menlo park or palo alto) -Result: 13 RESTAURANTS -> show eval $searchinfo(1,command) FIND cuisine french -> show eval $searchinfo(2,com) AND (city menlo park or palo alto)
$SEARCHINFO returns a null value when the integer does not correspond to a previously-issued search command. For instance, in the example above, $SEARCHINFO(3,com) would be null.
---------------------------------------- Function: $SEARCHTEST ($SRCHTEST) Purpose: Test the syntax of the value as a search expression; check processing rules ----------------------------------------
The $SEARCHTEST function is a one-argument function that takes the form:
$SEARCHTEST(search-command)
where "search-command" is a string containing a SPIRES search command expression, as described below.
SPIRES will check that the statement is valid as a search command for the selected subfile, including passing any search values in the expression through appropriate Searchproc rules. If the expression is valid, the function returns the value "0" (zero) as an integer. A non-zero integer returned by the function indicates an error, which can be further tested with the $MNUM and $ENUM variables. (If $MNUM is 0 when the function returns a non-zero integer, then either searching isn't allowed for the subfile, or the "search-command" string is null; note, however, that these conditions may cause other $MNUM values too.)
The "search-command" in the function should begin with a search command "verb": FIND, AND, OR, AND NOT, NOT (or an allowed abbreviation or symbol). If it does not begin with one of those, SPIRES will add an OR to the front. What follows should be a valid search expression, including search terms (index names), relational operators, and search values. Complex search expressions (search expressions joined with AND, AND NOT and OR, and/or including parentheses) are also allowed, as are expressions with qualifiers and sub-indexes.
Here is an example using a subfile with a KIDS index (Yes/No values) and a JOB index:
-> show eval $srchtest("find kids yes") 0 -> show eval $srchtest("find kids yeah") -Element=CHILDREN: -Serious data error, code=E46 1 -> show eval $srchtest("find kids yes & (job clerk or poet)") 0
This function is particularly handy for checking a search expression without having to incur the expense of SPIRES actually examining index records and creating the search result, which is useful in situations where you want to collect the search commands at one time but have the search done at a later time (perhaps during off-peak hours when rates are cheaper). $SEARCHTEST thus gives you the chance to check that the search commands are valid at the time the user gives them to you, but the search itself is not done till later.
---------------------------------------- Function: $SET Purpose: Set the value of a system variable: $ASK, $PARM, $PROMPT or any Prism system variables ----------------------------------------
The $SET function is a two-argument function that takes the form:
$SET(system-variable-name, expression)
where "system-variable-name" is a string containing the name ASK, PARM, PROMPT or a Prism system variable, such as $MSGLINE, and "expression" is a valid expression that will be converted to a string value and assigned to the system variable named in the first argument.
The function returns the string expression as its value. If the system variable name given is invalid, a null string is returned.
The purpose of this function is to make it easier to assign values to particular system variables. In particular, this lets you set Prism variables from inside Userprocs in record definitions.
Additionally, it also lets you set the value of $ASK or $PROMPT to a variable when that variable's value is an expression that the SET ASK or SET PROMPT command can't handle.
Here is an example showing how easy it is to set the $PROMPT variable with $SET when the value is an expression:
-> let x = 'Question 3. How many is 4+8? ' -> /set prompt = #x -Unrecognized: 3. How many is 4+8? -> eval $set(prompt, 'Question 3. How many is 4+8? ') -> show eval $prompt Question 3. How many is 4+8? ->
----------------------------------------- Function: $SETPARMS Purpose: saves its parameter values internally until retrieved by $GETPARMS function Related functions: $GETPARMS, $REF -----------------------------------------
$SETPARMS is a multi-argument function that evaluates and saves its parameter values for later retrieval by $GETPARMS, usually in a subroutine.
The function's form is as follows:
$SETPARMS(expression1, expression2...)
where
------------------------------------- Function: $SIZE Purpose: Determine a value's string length -------------------------------------
$SIZE is a single argument function of the following form:
$SIZE(input-value)
The function returns an integer representing the length of the input value, were it converted to string:
-> let z = sizetest -> show eval $size(#z) 8
----------------------------------------------- Function: $SPAN Purpose: Return substring of input value broken to the left of any character not also found in a second string Related functions: $BREAK, $STRIP -----------------------------------------------
The $SPAN function complements $BREAK in a way, matching its input string against an inclusion list instead of an exclusion list. (It also complements the $STRIP function, as described further below.)
The function takes the following form:
$SPAN(input-string,string2)
$SPAN scans the input string looking for characters that are NOT present in string2. The presence of a character in the input string that is not in string2 produces a break in the first string. The function then returns the substring of the input string up to but not including the character that had no match in the string2 list.
If the first character in the input string fails to find a match in string2, then $SPAN returns a null value.
For example:
-> let Id = 348089MATH -> let Numeric = 1234567890 -> show eval $span(#Id,#Numeric) 348089 -> show eval $strip(#Id,#Numeric) MATH
Note that $STRIP complements $SPAN by returning the part of the input value that $SPAN discards (and discarding the part that $STRIP returns).
--------------------------------------------- Function: $SQRT Purpose: Take square root of numeric value Related functions: $EXP, $LOG, $MOD ---------------------------------------------
$SQRT is a single argument function of the form:
$SQRT(real-value)
$SQRT takes the square root of the argument and returns a REAL result. If the argument is negative, $SQRT takes the square root of the positive equivalent.
Here are some examples of $SQRT:
$SQRT function Real Result -------------------- ----------- $SQRT(10) 3.16228 $SQRT(2) 1.41421 $SQRT("-3.1415") 1.77243
------------------------------------------- Function: $SQU Purpose: Delete blanks from a string value -------------------------------------------
The $SQU function allows you to delete extra blanks from a string value. Its syntax is:
$SQU(value)
All leading and trailing blanks will be removed and all multiple blanks in the value will be converted to single blanks. A string value is returned.
For example,
$SQU(" This is a test string value.")
will return
This is a test string value.
--------------------------------------- Function: $SSW Purpose: Determine whether a secure- switch is set for the selected file ---------------------------------------
The $SSW function (where SSW stands for "secure-switch") provides a way for you to tell whether your currently selected subfile has a particular secure-switch set. The function is thus somewhat similar to the SHOW SSW command. [For more information on secure-switches, see the manual "SPIRES File Definition", or online EXPLAIN SECURE-SWITCHES STATEMENT.]
$SSW is a single-argument function, whose form is as follows:
$SSW(ssw-integer)
"Ssw-integer" would be an integer corresponding to one of the secure-switches. (E.g., $SSW(9) corresponds to secure switch 9.) If the subfile has the named secure switch set, the argument is returned by the function; otherwise the function returns a "0" (zero).
For instance, suppose your currently selected subfile has secure switch 3 set, but not secure switch 4:
-> show eval $ssw(3) 3 -> show eval $ssw(4) 0
The value "0" will also be returned if the argument is not the number of a valid secure switch.
----------------------------------------------- Functions: $STATGET, $STATPUT Purpose: Control static variables (e.g., from within a format or Userproc) Related functions: $DYNGET, $DYNPUT, $VARGET, $VARPUT -----------------------------------------------
The $STATGET and $STATPUT functions help control static variables from inside SPIRES formats or file definition Userprocs where you need access to vgroups allocated outside of the format or Userproc.
The forms of the functions are:
$STATGET(variable-name,subscript,default-value) $STATPUT(variable-name,subscript,assigned-value)
"Variable-name" is the name of the variable to be processed -- its form is analogous to that of the variable named in a LET statement. (Normally, the name is not preceded by a pound sign "#"; if there, SPIRES will replace the name with the variable's value before executing the function, as you'd suspect.)
"Subscript" is the subscript of the variable; use a subscript of "0" or null if the variable is not in an array.
The third argument varies depending on the function. For $STATGET, the third argument is a default value that the function will return if the variable doesn't exist in any allocated vgroup it can access (see below). For $STATPUT, the third argument is the value you want to assign to the variable.
The functions allow a user to retrieve or replace the value of a static variable from inside a format or Userproc. They are most useful when the array the variable is in was allocated globally but outside of the format or Userproc; in those cases, you otherwise cannot access the variable.
In a protocol, the $STATGET function lets you see if a variable with a particular name exists. If the named variable does not exist in an allocated vgroup, then the function returns the default-value.
The function $STATPUT allows you to assign a value to a static variable from inside a format or a Userproc, especially when the variable is in a global vgroup allocated outside of the Userproc or format. The function returns the value of the third argument.
For example:
LET X = $STATPUT(Z,0,$INT(5))
will assign the value 5 to both #X and #Z.
------------------------------------------------ Function: $STRING Purpose: Convert a value's type to STRING Related function: $CHARACTER ------------------------------------------------
The $STRING ($STR) function is a single-argument function of the form:
$STRING(input-value)
$STRING converts its input value to a value of type string.
-> let x = $string(4-2)
Note that in many cases the function is superfluous for string conversion, because SPIRES would convert a non-string value to a string automatically. For instance, the slash ('/') prefix automatically converts variables that follow it into variable-length strings. [See 5.17.] In addition, any function that requires a string argument (such as $LEFTSTR in the example below) will always convert a non-string into a string, if such a conversion is possible.
-> let Total = 400-200 -> show eval $type(#Total) PACK -> let Tenth = $leftstr(#Total,2) -> show eval #Tenth 20 -> show eval $type(#Tenth) STR
An argument in quotation marks or apostrophes is automatically considered a string value. If you attempt to convert such a value with $STRING, no actual conversion occurs. Likewise, if you attempt to convert the value with $CHARACTER, only the type is changed; the value itself is unaffected. [See 6.2.] For more on the distinction between the $CHARACTER function and $STRING, see $CHARACTER.
---------------------------------------------- Function: $STRIP Purpose: Strip away beginning of string value Related functions: $BREAK, $RIGHTSTR, $RIGHTSUB, $SPAN ----------------------------------------------
The $STRIP function, which creates a substring out of its input string by stripping away the initial characters from it, has the following form:
$STRIP(input-string,string2)
$STRIP scans its input string from left to right looking for characters that are also present in string2. When a character is encountered in the input string that is NOT also in string2, the function stops scanning, and returns the characters remaining in the input string.
The $SPAN and $STRIP functions are exactly complementary:
-> show eval $strip(114ART,1234567890) ART -> show eval $span(129HISTORY,1234567890) 129
------------------------------------------------ Function: $SUBSTR Purpose: Make shorter substring of string value Related functions: $LEFTSTR, $LEFTSUB, $RIGHTSTR, $RIGHTSUB, $XSTR ------------------------------------------------
$SUBSTR is a 3-argument function for creating a substring out of the input string given. The function has the form:
$SUBSTR(input-string,integer2,integer3)
$SUBSTR works as follows. First an "integer2" number of characters are skipped to determine the substring's starting point. Then exactly "integer3" characters are returned as the substring. [If integer2 is negative or skips all the characters in the input string, then a null string is returned regardless of the value of integer3. If integer3 is negative or larger than the number of characters remaining in the input string, then the remaining characters are returned.]
Following are some examples of $SUBSTR:
$SUBSTR function Result ------------------------ -------- $SUBSTR(USERID,4,2) ID $SUBSTR(userid, 0, 4) user -> show eval $filename GQ.DOC.SPIKE.EXPLAIN -> show eval $substr($filename,7,5) SPIKE
-------------------------------------------------- Function: $SYSEVAL Purpose: Pass expression to WYLBUR for evaluation --------------------------------------------------
The $SYSEVAL function lets you pass an expression (for instance a WYLBUR variable or function) to WYLBUR to be evaluated there; the function returns a string value that is WYLBUR's evaluation of the expression. [See 5.1.1 for more on the concept of expressions.]
The form of the function is as follows:
$SYSEVAL(expression)
where "expression" represents an expression recognizable to WYLBUR.
The returned value will be truncated to 158 characters.
Here is an example of the $SYSEVAL function:
-> show eval $syseval(date) 16:13:56 04/18/86 (86.108)
In this case $SYSEVAL asks WYLBUR to evaluate the WYLBUR variable DATE. A user within WYLBUR who issued the SHOW DATE command would obtain the same value as shown above.
$SYSEVAL can be a handy way to pass values on to SPIRES that the end-user or application has stored in WYLBUR variables earlier in the session. (These values may well have been established before SPIRES was even called).
There are many other possible uses as well:
-> if $syseval(lines) ~= 0 then ... <---(tests for presence of an active file)
In the example below, a user in SPIRES borrows the WYLBUR function MIN, which has no exact equivalent in SPIRES, to find the minimum value in a set of numerical values stored in a dynamic array:
-> show dynamic variables N::3 = 3 N::2 = 2 N::1 = 1 -> /let leastval = $syseval('min(#n::1, #n::2, #n::3)') -> show eval #leastval 1
The system variable $MSGINT records the success or failure of $SYSEVAL. When the function succeeds, $MSGINT is set to 0 (zero); if it fails, an error message is returned and $MSGINT is set to a non-zero value. You can test the variable in your code:
let author = $syseval(author) if $msgint ~= 0 then ... <---(take action if the function fails)
-------------------------------------------------- Function: $SYSINFO Purpose: Obtain information on the current system --------------------------------------------------
The single-argument function $SYSINFO, primarily designed for systems programmers, returns information about the SPIRES or SPIRES-related system that is currently executing. For instance, $SYSINFO returns information about the release date or the link date applying to the currently-executing version of SPIRES.
The form of the function is as follows:
$SYSINFO(code)
The "code" argument can take the following values:
Code Value Returned by $SYSINFO --------- ------------------------------------------- PROCESSOR The processor currently running -- e.g., SPIRES, SPIBILD LINKDATE The date (yy.mm.dd) at which this processor was last linked as a separate module SYSTEM The version of the system. Possible values returned for $SYSINFO(SYSTEM) include: Prod Production System PreProd Pre-Production Test Test System Dev Development System Private Private System Batch Batch SPIRES RELEASE The system's release date (yy.mm) ACCOUNT The account upon which the system was linked
Values for code can be abbreviated to three characters, e.g.:
-> show eval $sysinfo(rel) 87.06
If the "code" you enter is invalid $SYSINFO returns a null string.
------------------------------------------ Function: $SYSTEM Purpose: Retrieve fully qualified names for files, formats, etc., or validate accounts ------------------------------------------
Note: $SYSTEM is designed primarily for SPIRES systems programmers, though the feature for account validation may have a wider use.
The $SYSTEM function retrieves fully qualified names for SPIRES files, global and general file formats, etc., using the abbreviated name provided. It can also be used for account validation as described further below.
The syntax of $SYSTEM is:
$SYSTEM(string,code)
where the string often begins with an asterisk, period or dollar sign, and the code is optional. If no code is provided, the function modifies the string as follows: If it starts with a dollar sign, the dollar sign is converted to the system account, followed by a period. If the string starts with an asterisk or a period, that character is changed to the logged-on user's account, followed by a period. If the string consists only of a period or asterisk, then the appended period is omitted. If the string begins with anything else, it is preceded by the logged-on user's account number, followed by a period.
Additional characters are prefixed to the string if one of the following codes is given:
FULL - string is preceded by "ORV." GLOBAL - string is preceded by "ORV.", unless the string begins with a "$", in which case only the string is returned GENFILE - string is preceded by "**ORV.", unless the string begins with a "$", in which case only the string is returned
An additional code is available for account validation:
ACCOUNT - the function verifies that the string is in a valid form for an account (ORVYL-STS system) or userid (CMS, MTS, TSO) on the current operating system (see below)
The codes may be abbreviated to three characters.
For example, if the currently logged-on account is GQ.TSI:
$SYSTEM('FILEDEF') = GQ.TSI.FILEDEF $SYSTEM('*') = GQ.TSI $SYSTEM('*FILEDEF') = GQ.TSI.FILEDEF $SYSTEM('$FILEDEF') = GG.SPI.FILEDEF $SYSTEM('*FORM',FULL) = ORV.GQ.TSI.FORM
At STS sites (i.e., Stanford), the ACCOUNT code on the $SYSTEM function verifies that the string given in the first argument is six characters long. (If there is a seventh character it must be the separator character, i.e., a period.) The first and fourth characters must be alphabetic, the second, fifth and sixth characters must be alphanumeric, and the third character must be a period. If the string passes this test, the function returns it in upper case. If the string fails the test, $SYSTEM returns a null string.
------------------------------------------- Function: $TEST Purpose: Test whether a condition is true or false Related function: $EVALUATE -------------------------------------------
$TEST is a one-argument function, not commonly used, that tests whether a condition is true or false. Its form is as follows:
$TEST(condition)
where:
The example below indicates how $TEST might be used:
LET X = "4 > 3" IF $TEST(#X) THEN SHOW EVAL #X ' is true.'
Note that $TEST is not simply testing whether #X is null or non-null (which could be done with a statement beginning IF #X...), but is testing whether the expression contained in the variable is true or false.
In a protocol, expressions can be tested in a similar way with the slash prefix '/':
/IF #X THEN SHOW EVAL #X ' is true.'
Thus $TEST will be most valuable for testing expressions in formats and USERPROCs, where the '/' prefix is not allowed.
Note that $TEST can also be used to assign a flag value to a variable:
LET FLAGTEST = $TEST(#X)
----------------------------------------- Functions: $TIMEIN and $TIMEOUT Purpose: Process a time value -----------------------------------------
The $TIMEIN and $TIMEOUT functions convert time values; specifically $TIMEIN converts a time string to an integer value in millisecond units, and $TIMEOUT converts an integer value in millisecond units (or, less commonly, a hex value in 2-second units) to a time string in one of the standard forms shown in the chart later in this section. Along with many other uses, the two functions can be used together to convert a time value from one format to another (see further below).
The syntax for the $TIMEIN function is:
$TIMEIN(time-string)
The "time-string" can be any of a wide variety of formats for time, such as "11:20:55" or even "4 Days, 11 Hours, 20 Minutes, 55 Seconds". Note that if only one number is present, SPIRES assumes it is hours; for two, hours and minutes. In other words, $TIMEIN("3") would interpret "3" as "3 hours", or "3:33" as "3 hours, 33 minutes". See the chart below under "Format" for other appropriate formats.
You may also use the words "noon" or "midnight", optionally preceded by the number "12" (or 12:00 or 12:00:00), as the time-string; the case of NOON or MIDNIGHT is not significant.
Aside from the NOON and MIDNIGHT words, be careful not to mix colons and words in the same time-string, and do not use a suffix such as "PM" together with a value for hours that is greater than 12. The maximum time allowed in a time-string is over 24 days; a negative time-string will cause an error and return a null value.
The $TIME variable can be used as input to the $TIMEIN function.
The syntax for the $TIMEOUT function is as follows:
$TIMEOUT(time,typecode)
"Time" is most commonly a 4-byte integer in millisecond units. Among other possibilities, it might be the $GETUVAL of an element stored in 4-byte integer form by the $TIME system proc, or it might come from the $TIMEIN function described above. (Less commonly, the input for "time" is a 4-byte or 2-byte hex value, which is interpreted to be in 2-second units.)
"Typecode" corresponds to one of the values shown on the left in the chart below; each typecode specifies a different string format for the output time value. For example if the current value of $TIME is 14:30:25, then using typecode 6 with $TIMEOUT will return the value "14 Hours, 30 Minutes, 25 Seconds". If an error occurs, the string returned by $TIMEOUT will be null.
Type- code Format Example (1:58:04 p.m.) _____ _____________________________________ ______________________ 0 [d Days,] hh:mm:ss[.s] 13:58:04 1 [d Days,] hh:mm(.m) 13:58.06667 2 [d Days,] mmm:ss(.s) 838:04 3 hh:mm:ss(.s) (AM|PM) 01:58:04 PM 4 hh:mm(.m) (AM|PM) 01:58.06667 PM 5 h(.h) (AM|PM) 1.9677778 PM 6 [d Days,] h Hours, m Minutes, 13 Hours, 58 Minutes, s(.s) Seconds 4 Seconds 7 [d Days,] h Hours, m(.m) Minutes 13 Hours, 58.06667 Minutes 8 [d Days,] h(.h) Hours 13.9677778 Hours 9 d(.d) Days 0.58199074 Days 10 hhh Hours, m Minutes, 13 Hours, 58 Minutes, s(.s) Seconds 4 Seconds 11 HHH(.h) Hours 13.9677778 Hours 12 mmm Minutes, s(.s) Seconds 838 Minutes, 4 Seconds 13 mmm(.m) Minutes 838.06667 Minutes 14 sss(.s) Seconds 50284 Seconds
NOTE: Typecode 15 is the same as typecode 6, but only non-zero units are displayed.
The parenthetical values shown in the chart are only output when the value is non-zero. Decimal points indicate fractional values (output only when the fraction would be non-zero), and triple character designators such as mmm represent values that include all time units greater than this unit. (That is, "2 hours" becomes represented in mmm form as "120".)
The two functions can be used together to convert a time value from one format to another:
$TIMEOUT($TIMEIN('time-string'),typecode)
Here are a few brief examples of converted formats, output over a five-second interval at about 2:30 in the afternoon:
-> show eval $time 14:33:20 -> show eval $timeout($timein($time),15) 14 Hours, 33 Minutes, 23 Seconds -> show eval $timeout($timein('14:33:25'),2) 873:25
Note that the "typecode" you specify may shift a value, so that what you input as hours comes out expressed in minutes. For example, a "typecode" of 2 takes a value input as hours and re-formulates it in terms of minutes.
---------------------------------------------- Function: $TRANINFO Purpose: return information about status of the currently open transaction group ----------------------------------------------
The $TRANINFO function is a one-argument function of the form:
$TRANINFO(code)
where "code" is one of the following:
---------------------------------------------- Function: $TRANSLATE Purpose: Translate a string value Related functions: $CASE, $CHANGE, $CHANGELIST, $UNEDIT ----------------------------------------------
The $TRANSLATE function is a three-argument function of the form:
$TRANSLATE(input-string,string2,string3)
The value of the input string is altered by replacing any characters in string2 that occur in the input string by the corresponding character in string3.
For example:
$TRANSLATE(1234,123456,654321) returns 6543 $TRANSLATE(123444,1234,21) returns 21
String2 and string3 are often the same size. If their size does not match, then the missing character positions are changed to null. Therefore, in the second example, 3 and 4 in argument 2 are changed to null in argument 1 because there are no characters in their positions in string3.
------------------------------------------- Function: $TRIM Purpose: Convert a value to type CHAR. -------------------------------------------
The $TRIM function is exactly equivalent to $CHARACTER.
--------------------------------------------- Function: $TRUNC Purpose: Truncate a real value to its integer portion ---------------------------------------------
$TRUNC is a one-argument function of the form:
$TRUNC(real-value)
where "real-value" is a real (floating-point) value or a value that is convertible to real. $TRUNC returns the integer portion of the value; that is, the fractional part of any real value is dropped. The returned value is type integer.
Here are some examples of $TRUNC:
$TRUNC(5.1) = 5 $TRUNC(5.0) = 5 $TRUNC(0.3) = 0 $TRUNC("-5.1") = -5 $TRUNC("-0.3") = 0
------------------------------------------- Function: $TYPE Purpose: Test a value's type Related functions: $TYPETEST, $RETYPE -------------------------------------------
$TYPE is a single argument function returning a character string that represents the data type of the value specified in the argument. (Possible data types are string, integer, hex, real, and so on.)
Here is an example of $TYPE:
-> let number = 2 -> show eval $type(#number) STR -> let number = $int(#number) -> show eval $type(#number) INT
------------------------------------------------ Function: $TYPETEST Purpose: Test conversion of a value's data type Related functions: $RETYPE, $TYPE ------------------------------------------------
The $TYPETEST function is a two-argument function that checks to see if the first argument, a string or a variable, might be successfully converted to the variable-type specified in argument 2. If argument 1 could be converted to the type specified in argument 2, then the function returns the value of argument 2; if argument 1 could not be converted to the type specified by argument 2, $TYPETEST returns a null value. In other words, you could use the $TYPETEST function to determine if you would encounter a conversion error if you tried to convert a value to a different type.
For example,
LET M = $TYPETEST(123,INT) ... M is set to 'INT' LET M = $TYPETEST(ABC,INT) ... M is set to null
SPIRES only reads the first 3 characters of the second argument and interprets the code as follows:
CHA = character FLA = flag HEX = hex INT = integer LIN = line PAC = packed REA = real STR = string WDS = wds REF = reference
GRO = group NEW = new TRI = triple
These last three codes are used with triples.
All codes may be abbreviated to one or two characters. The variable $TYPETEST can be used as a TRUE/FALSE condition in an IF statement, because a null return will be interpreted as FALSE while any other returned value will be interpreted as TRUE.
Like the EVAL command [See 5.16.] The $TYPETEST function does not actually perform variable conversion; unlike EVAL, it does not set $NO to TRUE if argument 1 could not be converted to the type specified in argument 2.
Note that a null value (for argument 1) can convert to all types except REF, GRO, NEW and TRI.
------------------------------------------------- Function: $UNEDIT Purpose: Strip edit mask characters from a value. Related function: $CHANGE, $EDIT, $STRIP, $TRANSLATE -------------------------------------------------
The $UNEDIT function allows you to strip a value of all the special characters added by the $EDIT function, such as the dollar sign, commas, "CR". It works somewhat similarly to the $UNEDIT system proc. [For more information on edit masks, see the manual "SPIRES Technical Notes" or online EXPLAIN EDIT MASKS; for more information on the $UNEDIT system proc, see the manual "SPIRES System Procs" or online EXPLAIN $UNEDIT PROC.]
The form of the function is as follows:
$UNEDIT(input-value)
where "input-value" is the string to be converted into "unedited" form. The resulting value will be of type string, but it can be converted to a packed decimal value.
For example,
$UNEDIT('$5,235') returns 5235 $UNEDIT('$45.99') returns 45.99 $UNEDIT(' $123.47 CR') returns -123.47
----------------------------------------------- Functions: $VARGET, $VARPUT Purpose: Control variables (e.g., from within a format or Userproc) Related functions: $DYNGET, $DYNPUT, $STATGET, $STATPUT -----------------------------------------------
The $VARGET and $VARPUT functions can be used to fetch and store variable values. They are particularly useful from inside SPIRES formats or file definition Userprocs in situations where you need access to dynamic variables or to static variables allocated outside of the format or Userproc.
These functions differ from the $DYNGET/$DYNPUT and $STATGET/$STATPUT pairs primarily in the order of their parameters; because their parameter lists end with optional, multiply-occurring "subscript" parameters, $VARGET and $VARPUT allow you to work with multi-dimensional static arrays, which $STATGET and $STATPUT do not easily permit. You may find the order of the parameters on $VARPUT and $VARGET easier to remember.
Also, of course, the $VARPUT and $VARGET functions let you work with any variable, not dynamic variables alone ($DYNGET/$DYNPUT) or static variables ($STATGET/$STATPUT).
The forms of the functions are:
$VARGET(variable-name, default-value, subscript(s)) $VARPUT(variable-name, assigned-value, subscript(s))
"Variable-name" is the name of the variable to be processed -- its form is analogous to that of the variable named in a LET statement. (Normally, the name is not preceded by a pound sign "#"; if there, SPIRES will replace the name with the variable's value before executing the function.)
The second argument varies depending on the function. For $VARGET, the second argument is a default value that the function will return if the variable doesn't exist as a dynamic variable or in any allocated vgroup that the function can access (see below). The second argument is optional for $VARGET.
For $VARPUT, the second argument is the value you want to assign to the variable. It is required for $VARPUT.
The third and subsequent arguments, "subscript(s)", are optional for both functions. If omitted, then the first occurrence of the variable is assumed. But for multiply-occurring variables, and static variables in multi-dimensional arrays, the subscript arguments specify the desired occurrence.
The $VARGET function returns the value of the requested variable in the same type in which it is stored.
The $VARGET function is also useful for determining whether a variable with a particular name exists. If the named variable does not exist, then the function returns the default-value.
The function $VARPUT allows you to assign a value to a static variable from inside a format or a Userproc, especially when the variable is in a global vgroup allocated outside of the Userproc or format. The function returns the value of the second argument; its type is the same as the type of the second argument.
For example:
$VARPUT(Z,$INT(5))
will assign the value 5 to #Z as an integer (assuming #Z is not defined in an allocated vgroup as a different type), and will return the value 5 as an integer. Continuing the same example:
$VARGET(Z)
will return the value 5 as an integer.
If the variable you are retrieving or storing is in a multi-dimensional static array, you can name the absolute occurrence number of that variable within the array as the first and only subscript number. For example, if ABC is a two-dimensional array with two occurrence per dimension, then the command SHOW STATIC VARIABLES will display the array like this:
ABC::0 = 392 ABC::1 = 4028 ABC::2 = 9227 ABC::3 = 329
You could retrieve the final value in the array with either of these functions:
$VARGET(ABC,,1,1) $VARGET(ABC,,3)
(As usual, remember to count occurrences of variables from zero.)
------------------------------------------------- Function: $VARTEST Purpose: Determine attributes of static variables Related function: $TYPETEST, $TYPE -------------------------------------------------
The $VARTEST function provides several types of information about attributes of allocated static variables, such as their type, their length, maximum number of occurrences, etc.
The $VARTEST function's syntax is:
$VARTEST(variable-name, code) or $VARTEST(variable-name, code, index)
where "variable-name" is the name of a static variable in an allocated vgroup. (Don't put the "#" ahead of the name unless you want substitution to take place before the function is evaluated; that is, unless the variable in the function contains the name of the variable you really want the information for.) The "code" is one of the following:
The second form of the function, with the "index" parameter, is for use with variables with the INDEXED-BY statement coded in their definitions. The "index" is an integer from 1 up that represents a particular array dimension for the named variable. So, for example, if variable ABC is defined as "INDEXED-BY = DIMENSION1, DIMENSION2;", then you would specify "1" as the index value to get information about #DIMENSION1 as an indexing variable to ABC.
Only two of the codes listed above work with that form: OCCURRENCES, which shows the number of occurrences of the variable for the specified "index" dimension; and INDEXED, which names the variable that serves as the index for the specified "index" dimension.
Here are some examples of $VARTEST and the values it would return. Suppose you have an allocated vgroup that contains the following variable (shown here in its variable definition):
VARIABLE = StudentCounts; OCCURS = 4,2,8; TYPE = INT; INDEXED-BY = CLASS, SEX, DEPT;
Here is $VARTEST in use:
$VARTEST(StudentCounts, TYPE) = INT $VARTEST(StudentCounts, OCC) = 64 (4x2x8) $VARTEST(StudentCounts, OCC, 1) = 4 $VARTEST(StudentCounts, INDEXED) = CLASS, SEX, DEPT $VARTEST(StudentCounts, INDEXED, 2) = SEX
------------------------------------------------- Function: $VERIFY Purpose: Verify characters in input string are all like or unlike characters in a second string -------------------------------------------------
The $VERIFY function compares all the characters in the input string with the characters in a second string; depending on what you request, it verifies either that all the characters in the input string can be found in the second string (LIKE), or that none of them can be found there (UNLIKE).
The $VERIFY function's syntax is:
$VERIFY(input-string,LIKE,characters) or $VERIFY(input-string,UNLIKE,characters)
where "input-string" is the string whose characters you want to check, and "characters" is the list of characters to which to compare the input string.
The function returns "1" ($YES) if the function is satisfied; it returns "0" ($NO) if not. The function also returns "1" if the input string is null.
Here are a couple examples of its use:
$VERIFY($Ask,like,'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
That function is checking that $ASK contains only capital letters. Values for $ASK like "howdy" and "HELLO THERE" would fail (the latter because of the blank, which is not in the characters string); Values for $ASK like "GREETINGS" and "" (null) would pass.
$VERIFY($Val,unlike,'()!@# ,?$%.')
That function is checking to make sure that $VAL doesn't contain any of the special characters listed in the characters string, which you might notice includes a blank and a comma.
------------------------------------------------- Function: $VGROUPALTER ($VALTER) Purpose: change the size of an allocated vgroup by changing the size of an array Related functions: $VGROUPINIT ($VINIT) -------------------------------------------------
The $VGROUPALTER function lets you manage vgroup array space dynamically. It will reallocate the vgroup in memory, shrinking or enlarging the vgroup's size as necessary, changing the size of an array within the vgroup.
Here is the function's syntax:
$VGROUPALTER(vgroup-name, variable-name, occ-val)
where "vgroup-name" is the name of the static vgroup whose size you want to change. "Variable-name" is either the name of a one-dimensional array whose size you want to change, or is the name of an index variable for one or more multiple-dimension arrays in the vgroup. "Occ-val" is an integer (its value must be greater than 1) representing the number of occurrences you want for the one-dimensional array or the number of occurrences within the dimension of the index variable for the multiple-dimension arrays it indexes. (Note that it will take multiple uses of $VGROUPALTER to change multiple dimensions of an array.)
$VGROUPALTER is most often coded in EVAL commands or Uprocs; no value is returned by the function. If it fails, an S-level error is reported.
Suppose, for instance, that you will be reading data from a record into an array but the size of the array will vary widely, depending on the size of the record. You might initially allocate the array in the vgroup with the normal number of occurrences you expect for the array, and then, when you know a particular record will be too large, reallocate the vgroup with $VGROUPALTER to exactly the size you need. That way, you don't have to allocate perhaps hundreds of extra occurrences of the array that you will not need to handle the largest possible (but seldom occurring) record, which could waste many bytes of internal memory.
For a specific example of how to code $VALTER, suppose in vgroup LOCAL, you want to raise the size of two arrays that use the integer variable SUBCOUNT as an indexing variable. In their definitions, the two arrays each have 5 occurrences listed for the dimension indexed by SUBCOUNT, but you would like to bump that dimension to 10 occurrences. Here is the $VALTER function you would use:
$VALTER(LOCAL, SUBCOUNT, 10)
Important notes about $VALTER:
- Because $VALTER reallocates the entire vgroup, moving its internal location, any variables in the vgroup that are referenced outside of the vgroup will be lost as referenced variables when $VALTER executes.
- If you use $VALTER to shrink a vgroup, the extra occurrences (the higher-numbered ones) will be thrown away, whether or not they have values.
- The $VGROUPINIT ($VINIT) function will reallocate the vgroup to its original size.
- You cannot use $VGROUPINIT to change the size of read-only arrays. [See 4.1.1.]
- If misused or overused, this function will defeat its purpose, adding superfluous or expensive processing as well as possible core fragmentation. It is recommended that, as in the example above, you define arrays with occurrence counts that will handle the majority of cases, using $VGROUPALTER for the rare times when the normal size won't work.
- It is also a good idea to put any arrays that you want to alter into their own separate vgroup, to reduce the size of the vgroup being moved around by $VALTER.
- If you specify 1 or less as the "occ-val", which is not allowed, an S91 error will occur; if you specify an invalid vgroupname, an S177 error will occur; and if you specify an invalid array name or a variable that is not an "indexing" variable, an S798 error will occur.
------------------------------------------- Function: $VGROUPINIT ($VINIT) Purpose: Reinitialize the variables of the named vgroup Related function: $ASET, $VGROUPALTER -------------------------------------------
The $VGROUPINIT function, alias $VINIT, re-establishes the values that the variables in a particular vgroup had when it was first allocated. The function's form is:
$VGROUPINIT(vgroupname) or $VINIT(vgroupname)
where "vgroupname" is the name of the vgroup whose values are to be initialized. This name should be in one of the forms allowed for the SET VGROUP or ALLOCATE command, such as "ORV.gg.uuu.testvars". [See 4.2.1 for other forms of "vgroupname".] It is quite important to include the "ORV" prefix on the "vgroupname", if your vgroup will be allocated or used by another account besides your own. For example:
-> eval $vinit(orv.gq.pro.testvars)
$VGROUPINIT "zeroes out" the variables in the vgroup that it names. However, if any of the variables were given initial values in their vgroup definition (through the VALUE statement), $VGROUPINIT will reset the variables to those initial values. [See 4.1.1.]
Only the variables in the named vgroup are affected; no dynamic variables will be affected, including any declared in the vgroup definition. To clear dynamic variables, use the $ZAP or $DYNZAP functions, or the command CLEAR DYNAMIC VARIABLES.
-------------------------------------------- Function: $WDS Purpose: Convert a value's type to WDS Related function: $CHAR, $STRING --------------------------------------------
$WDS is a one-argument function, rarely used today, that converts its argument to type WDS.
For an explanation of $WINDOW, see $PRECISION function.
--------------------------------------------- Function: $WORKDAYS Purpose: Find a date x number of working days away Related functions: $DATEIN, $DATEOUT, $DAYS ---------------------------------------------
The $WORKDAYS function returns the date of the nth workday either before or after a specified date. It bases its calculations on days of the week (Monday-Friday) plus information on Stanford University holidays as found in the System Calendar.
The syntax of the $WORKDAYS function is:
$WORKDAYS(n,date)
where:
The number of working days may be a positive or negative number and it will return the date as a string in the form mm/dd/yy. If "0" is used, then the function will return a date value if "date" is a workday but null if it is not.
Examples of the $WORKDAYS function:
$WORKDAYS(1) <-- returns next working day from today $WORKDAYS(3) <-- returns third working day from today $WORKDAYS(-1) <-- returns previous working day from today $WORKDAYS(5,'12/22/92') <-- returns '12/31/92' (skipping Christmas holidays) $WORKDAYS(0,'1/15/93') <-- returns '01/15/93' because it's a workday $WORKDAYS(0,'1/17/93') <-- returns '' because it's a holiday (Martin Luther King day)
--------------------------------------------- Function: $XDATE Purpose: Reformat date value from days into standard date form Related functions: $DATEIN, $DATEOUT, $DAYS ---------------------------------------------
$XDATE is a one-argument function that takes an integer value representing the number of days since Jan. 1, 0000, and converts it into the 4-byte hexadecimal form of a date (in the form CCYYMMDD). It is used to reconvert values established by $DAYS and arithmetic into the standard SPIRES form of a date.
In the example below, the $DAYS and $XDATE functions take today's date (held in unconverted form in the $UDATE variable), and use it to establish the date five days into the future:
-> let x = $XDATE($DAYS($UDATE) + 5)
------------------------------------------- Function: $XEQSTACK Purpose: Tracks nesting of execution levels in protocols -------------------------------------------
The $XEQSTACK function returns the XEQ command that nested execution to the level specified in its argument. The function is especially useful for debugging applications with complex nesting of commands.
$XEQSTACK has the following form:
$XEQSTACK(integer)
where:
(A null response to $XEQSTACK indicates that there was no command at the specified level.)
For instance, consider a protocol that nests in the following way:
* LEVEL0 XEQ PROC LEVEL1 RETURN : ++LEVEL1 XEQ PROC LEVEL2 RETURN : ++LEVEL2 <-- At LEVEL2, $XEQSTACK(0) returns XEQ PROC LEVEL2 : $XEQSTACK(1) " XEQ PROC LEVEL1 RETURN $XEQSTACK(2) " XEQ FROM LEVEL0 (or XEQ USING if protocol executes from the active file)
Compare the SHOW XEQ STACK command, which displays the entire stack of XEQ commands at once. [See 3.2.2.] See also the $XEQLVL variable, which tells the level at which an executing protocol is nested. [See 6.4.]
The maximum number of nesting levels is 128.
---------------------------------------------- Function: $XSTR Purpose: Convert a string (or hex) value Some related functions: $LEFTSTR, $LEFTSUB, $RIGHTSTR, $RIGHTSUB, $SUBSTR ----------------------------------------------
The $XSTR function returns a substring of its input string, deleting the front or the back of the input value, depending on parameters that you specify. In addition, the function can be used to convert a hex value instead of a string value.
The syntax for the $XSTR function is as follows:
$XSTR(value,action,portion,n)
where
- KEEP (or K) -- to keep the identified portion and delete the rest; or
- DELETE or DROP (or D) -- to delete the identified portion, retaining the rest.
- FRONT or FIRST (or F) -- signifies the front end for string type value
- LAST (or L) -- signifies the back end for string type value.
- HF -- signifies the front end for hex type value.
- HL -- signifies the back end for hex type value.
Here are some examples of $XSTR at work. Assume #X = 'MISTRUST'.
$XSTR(#X,Keep,Front,4) = MIST $XSTR(#X,Keep,Last,4) = RUST $XSTR(#X,Delete,First,3) = TRUST $XSTR(#X,Keep,Front,0) = '' $XSTR(#X,Delete,Front,0) = MISTRUST
The function would work similarly with hex values.
Note that the combinations "Keep,First" and "Drop,First" make the function equivalent to $LEFTSTR and $RIGHTSTR respectively.
---------------------------------------------- Function: $XSUB Purpose: Get substring of value based on substring within the value Some related functions: $XSTR, $LEFTSUB, $RIGHTSUB ----------------------------------------------
Like $XSTR, the $XSUB function returns a substring of its input string, deleting the front or the back of the input value, depending on parameters that you specify. Unlike $XSTR, which uses physical character positions to determine what to return, $XSUB returns a substring of the original string that precedes or follows another substring specified in the function. So, for instance, $XSUB might return the value to the left of the first period in the input value, wherever it appears within the value.
In addition, the function can be used on a hex value instead of a string value.
The syntax for the $XSUB function is as follows:
$XSUB(value,side,substr-occ,substr)
where
- LEFT or L -- to keep the left side of the string, up to but not including the "substring"; or
- RIGHT or R -- to keep the right side of the string, just after but not including the "substring".
- FIRST (or F) -- signifies the first occurrence of the "substr" within the value
- LAST (or L) -- signifies the last occurrence of the "substr" within the value
- HF -- signifies the first occurrence for hex-type value.
- HL -- signifies the last occurrence for hex-type value.
Here are some examples of $XSUB at work. Assume #X = 'MISTRUST'.
$XSUB(#X,Left,First,'S') = MI $XSUB(#X,Left,Last,'S') = MISTRU $XSUB(#X,Right,First,'S') = TRUST $XSUB(#X,Right,Last,'S') = T $XSUB(#X,Right,First,0) = ''
The last example shows that $XSUB returns a null value if the "substr" cannot be found in the input string.
The function would work similarly with hex values:
-> show eval $udate 19910727 -> show eval $xsub($udate,right,hl,91) 0727
The combinations "Left,First" and "Right,First" make the function equivalent to $LEFTSUB and $RIGHTSUB respectively.
---------------------------------------------- Function: $YYCALC Purpose: Add or subtract years to a 2-digit year, or convert to a different format Some related functions: $YYTEST ----------------------------------------------
Arithmetic with years stored as 2-digit values (97 for 1997, for example) can run into problems at century boundaries, as the Year 2000 problems across the computer industry prove. The $YYCALC function supports basic addition and subtraction for two-digit years (in a variety of forms), providing wrap-around when the result crosses a century. Additionally, you can request the output in a different form than the input date, providing a means of converting a 2-digit year to another format; for example, you can request that the 2-digit year (99, perhaps) be output as a 4-digit one (1999). Some of the allowed forms for input and output allow an additional digit representing the quarter of the year (QYY or YYQ forms, for instance).
The syntax of the $YYCALC function is:
$YYCALC(year,increment,convert-form,wrap)
where:
(*) I - an integer from 0 to 199 representing the year 1900 plus the integer (e.g., 103 means 2003). Integers less than the "wrap" parameter value are bumped by 100 to place them in the window range. YY - a two-digit year, 00 to 99. If you input a single digit, a "0" is prefixed to make it a YY value. HY - a two-digit year, 00 to 99, continuing with sequentially higher values for the first "digit": first, hex FA for the first digit (hex FA concatenated to 0, hex FA concatenated to 1, etc.) representing 2000, 2001, etc. then hex FB concatenated to 0 (2010), up to hex FF concatenated to 9 (2059). If you input a single digit, a "0" is prefixed to make it a HY value. (See examples below about the hex forms for input and output.) YYQ - a two-digit year, plus a quarter (0 through 4) HYQ - the same as HY but followed by the quarter (0-4) (*) QYY - the quarter (0-4), followed by a two-digit year (*) QHY - the same as HY but preceded by the quarter (0-4) CCYY - a four-digit year, e.g., 1997 (*) TERM - a four-digit value of the form: CYRN (explained later).
The TERM form is a PeopleSoft variation of CCYY and Q called CYRN. On input, all values are first converted to CCYY and Q. Forms CCYY, I, YY and HY supply Q=0. If a TERM of the form CYRN is input, Q=N/2 and CCYY=CYR+2000-101. CCYY and Q are then converted to the requested output form. If a TERM is to be output, N=2Q, and CYR=CCYY+101-2000.
For example, $YYCALC(394,,QYY.TERM) creates 0956. That's because YY of 94 becomes CCYY of 1994 which becomes CYR of 095 when you add 101 and subtract 2000. Meanwhile, Q of 3 becomes N of 6.
The following table gives a range of examples:
YYQ TERM YYQ TERM YY TERM 981 0992 001 1012 95 0960 982 0994 002 1014 13 1140 983 0996 003 1016 984 0998 004 1018 CCYY TERM 991 1002 011 1022 992 1004 012 1024 1985 0860 993 1006 013 1026 2004 1050 994 1008 014 1028 2015 1160
Here are some sample calculations and conversions using $YYCALC. Note in all cases that the default "wrap" value of 30 is used, and that when no convert-form parameter is specified, the default output form is YY.
First are some basic examples:
$YYCALC(99,1) = 00 $YYCALC(99,1,ccyy) = 2000 $YYCALC(02,-5) = 97 $YYCALC(02,-5,ccyy) = 1997
The "wrap" value defines a specific 100-year range for input interpretation and allowed output for all forms except I and CCYY.
|------------wrap=30-----------------| |--------------wrap=50--------------| 1930 1950 1999 2000 2029 2049 ...
For instance, using the default "wrap" of 30, the allowed range is 1930-2029. If you add 1 to the input value of "29" (which, using the default "wrap" of 30, represents 2029) the result is 2030; if you request the YY form of output, 2030 is outside the 100-year range, and so a null value is returned, representing an error:
$YYCALC(29,1) = (null) <- Error, because the result, 30 (2030), is outside the range of years allowed for output in this form (1930-2029). $YYCALC(29,1,CCYY) = 2030 <- no error, since the CCYY output form does not have the 100-year range that YY does
Here you want to convert the input value from its QYY form to YYQ:
$YYCALC(384,,QYY.YYQ) = 843
Here you want to convert the input value to the number of years it represents since 1900:
$YYCALC(2013,,CCYY.I) = 113
In that particular case, the input-form is optional, since a 4-digit input value is assumed to be in CCYY form. Similarly, a 3-digit input value is assumed to be in YYQ or HYQ form, and a 2-digit input value is assumed to be in YY or HY form. For clarity's sake, you may want to declare the input form, as shown in the last example; however, be aware that if the input value does not match the form (for instance, for ccyy, if the input value had been 13 instead of 2013), then an error occurs, which would not happen if the input-form were omitted.
For type I, integer input values above 199 are treated as though they were the special value: 255. Likewise, the 3-digit input-forms allow 999 as a special value, and the 4-digit input-form allows 9999 as a special value. Attempts to increment (or decrement) these special values are ignored, and the output will be the special value associated with the output-form. For example, $YYCALC($INT(200),5,I.QYY) will create 999 as an answer. Likewise, $YYCALC(999,,YYQ.I) will create the integer value 255.
The three hex forms, HY, HYQ and QHY, appear as part of a special method of handling Year 2000 problems. In essence, they provide a trick answer to the question, "What two-digit number is greater than 99?" In a string value, the digits 0 through 9 are stored internally as F0 through F9; 99 is thus F9F9 in hex. To provide a two-character "number" that is higher than 99, SPIRES can follow hex F9 with hex FA, hex FB and so on, to hex FF; when these hex values are used as the first character in a two-character number, they extend the two-character number range up to 159. This lets applications store a two-digit representation of the year 2000 and higher that will sort after "99", unlike the simple wrap-around to "00" for 2000. (Of course, other steps must be taken for displaying these special hex dates correctly, or working with them in other ways.)
To describe the hex forms a different way, using the hex form HY as an example, the numbers include the YY range from F0F0 to F9F9, but then continue with FAF0 (representing 2000) through FFF9 (2059). For input, HY values are interpreted according to the "wrap" range as shown in this example with wrap=60:
|-------------wrap=60--------------| F0F1 F3F0 F5F9 F6F0 F9F9 FAF0 FCF9 FFF9 2001 2030 2059 1960 1999 2000 2029 2059
If you use a hex form, keep in mind that the $YYCALC function still has only a 100-year range for output, defined by the "wrap" parameter, as in:
|------------wrap=30-----------------| |--------------wrap=60--------------| F0F1 F3F0 F6F0 F9F9 FAF0 FCF9 FFF9 1901 1930 1960 1999 2000 2029 2059
So, for example:
$YYCALC(99,1,hy) = hex FAF0 <- actually, this is a string containing these 2 hex characters
$YYCALC(05,1,hy.hy) = hex FAF6 <- 05 is 2005 in HY or YY input for wrap >= 05.
But hex FAF5 would be invalid input for the yy.hy option because it is not a yy input ranging from 00 through 99.
The following chart shows corresponding output values in the range for wrap=30 using CCYY, HY, YY, and I output-forms. The notation <FAF0> is an alternative way of showing hex FAF0, which is equivalent to $RETYPE($HEX(FAF0),STRING).
CCYY HY YY I 1930 30 30 30 1999 99 99 99 2000 <FAF0> 00 100 2029 <FCF9> 29 129
The YY and HY forms also allow you to specify a quarter value, from 1 to 4 (with 0 or a blank, i.e., a two-digit value, allowed to indicate no quarter), useful in some applications that work with a quarter system. So, for instance, if the form is QYY, then 499 means "fourth quarter 1999".
Interpretation and meaning of the quarter indicator is relative to the application. For instance, a QYY of 499 may mean the last three months of the calendar year 1999 or the last three months of the school year 1999-2000 (i.e., June through August 2000). That is especially important to remember when you are using $YYCALC to do conversions:
$YYCALC(497,,qyy.ccyy) = 1997
Both the input and the output must be interpreted the same: if 497 means the 4th quarter of the school year 1997, then the 1997 result represents the school year 1997. The mistake to avoid is thinking that you are converting from the school year to the actual calendar year; in other words, it is a mistake to think that the example above shows that the 4th quarter of the school year 97 is in the calendar year 1997.
In performing calculations with quarter forms, SPIRES inserts a placeholder "0" for the quarter when an input value lacks one. This means that 970 or 97 will sort before 971, 972, etc. (This is more significant for the $YYTEST function, described in the next section.) [See 7.9.5c.]
---------------------------------------------- Function: $YYTEST Purpose: Compare 1 through 4 digit year values using a century wrap-around value Some related functions: $YYCALC ----------------------------------------------
When years are stored as 2-digit values (97 for 1997, for example), it can be problematic to compare them, since in some contexts an absolutely smaller number ("00", for instance) may represent an earlier year (1900) but in other contexts it may represent a later one (2000). The $YYTEST function provides a way to compare two years, each of two digits. It also allows an additional third digit on the dates, representing a quarter value, that is, the form HYQ, as in 971 for "first quarter 1997".
The syntax of the $YYTEST function is:
$YYTEST(year1,rel-op,year2,wrap)
where:
EQ - equal NE - not equal GT - greater than GE - greater than or equal to LT - less than LE - less than or equal to
All comparisons are done with CCYYQ values. HY and CCYY assume Q=0. HY and HYQ have the HY converted to CCYY form before the comparison, as shown in this example with wrap=60:
|-------------wrap=60--------------| F0F1 F3F0 F5F9 F6F0 F9F9 FAF0 FCF9 FFF9 2001 2030 2059 1960 1999 2000 2029 2059
If an error occurs doing conversions, the original input strings are compared.
The function returns $TRUE (1) if the comparison is true; it returns $FALSE (0) if it is not true.
Here are some sample comparisons using $YYTEST. Note in all but the last case that the default "wrap" value of 30 is used.
$YYTEST(99,gt,98) = 1 ($TRUE) $YYTEST(99,gt,00) = 0 ($FALSE) $YYTEST(00,gt,99) = 1 ($TRUE) $YYTEST(991,gt,984) = 1 ($TRUE) $YYTEST(001,gt,994) = 1 ($TRUE) $YYTEST(94,gt,932) = 1 ($TRUE) (1994 is greater than the 4th quarter 1993) $YYTEST(94,eq,941) = 0 ($FALSE) (1994 with no quarter is less than 1st qtr 1994) $YYTEST(31,gt,29) = 0 ($FALSE) (1931 is less than 2029) $YYTEST(1989,gt,1) = 0 ($FALSE) (1989 is less than 2001) $YYTEST(11,gt,05,10) = 0 ($FALSE) (1911 is less than 2005)
In this final example, the first value being compared starts in the QYY form, but is converted to the HYQ form by the $YYCALC function so that it can be used in $YYTEST for comparison with a user variable, #compdate, which has an HYQ value representing the first quarter of 2005:
$YYTEST($YYCALC(304,,qyy.hyq),gt,#compdate) = 0 ($FALSE)
In other words, the third quarter of 2004 is not greater than the first quarter of 2005.
--------------------------------------------- Function: $ZAP Purpose: Eliminate a dynamic variable or dynamic element Related function: $DYNZAP ---------------------------------------------
The $ZAP function eliminates the dynamic variable named with it in a LET statement. Its form is as follows:
LET dynvar = $ZAP
where "dynvar" is the dynamic variable to be eliminated. $ZAP eliminates the specified dynamic variable (and its value), thereby freeing the space that the variable was taking in computer memory. [See 8.2.]
Note that the $ZAP function has no arguments and must be the only term on the right side of the equal sign in a LET statement. Within a format or USERPROC, the $DYNZAP function must be used instead of $ZAP to eliminate a dynamic variable.
The $ZAP function can also be used to "undefine" a dynamic element when used in the DEFINE ELEMENT command:
DEFINE ELEMENT elemname AS $ZAP
where "elemname" is the name of the previously defined dynamic element. This can be done more straightforwardly with the CLEAR DYNAMIC ELEMENT command, however.
In SPIRES protocols and in most programming languages, "variables" are the association of two entities: the name of a field and its value. "LET DAY = MONDAY", for example, is the association of a field named DAY with a value MONDAY. A "triple" is fundamentally different: it allows the association of more than two entities; it is usually used to associate three entities -- hence the name "triple" or "3-tuple" -- though it can be used to associate two or more entities. A collection of triples can themselves be associated into an entity called a "group".
The three entities associated by a triple are called the "attribute", the "object" and the "value" for purposes of formal description and nomenclature. For example, four different triples might be made from the following data:
1. 2. 3. 4. Attribute: Birth Date Sex Birth Date Employee Number Object: Ken Chandler Ken Chandler Maggie Horton Maggie Horton Value: 07/27/45 Male 04/07/43 172426
Triples 1 and 2 might form one group, based on a common "object"; 3 and 4 another group, also based on a common "object"; and 1 and 3 another group, based on a common "attribute".
Within a triple, the attribute, object and value can be variables or values of any type, including triples themselves; when the component parts of a triple are themselves made up of one or more triples, then more than three entities can be associated. In this sense, triples are like multi-dimensional arrays where the indexes of the dimensions need not be integers.
Thus, triples are an association of three values, given that a value itself may be a triple. Groups are an association of "n" values, each of which must be a triple; groups are "n-tuples" which may be referenced like a list structure.
Once made (i.e., an association of entities having been constructed), triples can then be looked up (i.e., retrieved or evaluated) by specific criteria (attribute, object, and value specified) or generic criteria (attribute or object or value specified), or the triples can be combined in groups by criteria. The system functions and variables described in this chapter accomplish these manipulations.
Note that triples and groups are like dynamic variables in that they cannot be stored in a static VGROUP.
The $MAKE function is used to create or "make" a triple from three components; it is a three-argument function that returns the address of the triple.
$MAKE provides two ways of making a triple:
LET address = $MAKE(attribute,object,value) EVAL $MAKE(attribute,object,value)
For example:
LET TRIADD = $MAKE(SEX,'Ken Chandler',MALE) EVAL $MAKE('EMPLOYEE NUMBER','Maggie Horton',$INT(172426))
EVAL makes the triple but doesn't store the address. The address of a triple is like the social security number of a person: in itself it contains no useful data about the person, but it is useful as a unique identifier to use to look up other information. It is most common to use EVAL to make a triple; the address can be obtained later by the $LOOKUP function, [See 7A.7.] if necessary.
The address of a triple cannot be displayed by a * command. Use the SHOW TRIPLES command to display the address and the values associated with triples. [See 7A.13.] Alternatively, you can use the $RETYPE function (with HEX) to see a hexadecimal translation of the address: $RETYPE(address,HEX).
The address of a triple must be stored into a dynamic variable, if it is stored at all. In the above example, if the TRIADD variable were to be in a VGROUP, the variable must be declared as TYPE=DYNAMIC. Storing the address of a triple into a dynamic variable causes the variable to take on TYPE=TRI:
-? let triadd = $make(sex,'Ken Chandler',male) -? let temp = $type(#triadd) -? / * #temp * TRI -?
$MAKE unconditionally creates the triple, whether or not it existed before; use the $MADE function to determine if the triple already exists. [See 7A.4.]
Note that the exact form of the components is important; "Birth Date", "BIRTHDATE" and "BIRTH DATE" are three different attributes. Also note that components with special characters (including blanks) must be enclosed in apostrophes or quotes as with other SPIRES functions.
$NEW is a variable that returns a number 16 greater than the last time it was used. This variable is useful in making a unique triple when fewer than three components are available.
For example, the following three commands make three unique triples:
EVAL $MAKE(FRIDAY,31,$NEW) EVAL $MAKE(THURSDAY,30,$NEW) EVAL $MAKE(THURSDAY,30,$NEW)
$NEW is generally used in functions associated with triples, as shown above; but the value of $NEW may be assigned to a dynamic variable for subsequent use:
-? let val = $new -? let temp = $type(#val) -? / * #temp * NEW -? eval $make(a,b,#val) -? eval $make(c,d,#val) -?
The $ANY variable is used in triples functions in much the same way the "?" is used in the $MATCH function: $ANY specifies that anything in that position qualifies. $ANY can be substituted for the attribute, object, and/or value specifications in functions associated with triples, to allow specification of "generic" criteria when used in place of one of the components. Note that $ANY, when used, must entirely replace a component.
When $ANY is used to specify generic criteria in triples functions, it causes a scan of all triples to locate those that satisfy the criteria. This is less efficient than the use of specific criteria for the attribute, object, and value, in which case no scanning is done by the system to locate the triple.
$ANY is a variable of TYPE=NEW.
The $MADE function returns the integer number of triples that satisfy the generic or specific criteria that are the arguments of the function; $ANY [See 7A.3.] is used to specify generic criteria.
$MADE is a three-argument function of the form:
LET integer = $MADE(attribute,object,value)
where attribute, object, and/or value can be "$ANY".
For example:
-? eval $make('Birth Date','Ken Chandler','07/27/45') -? eval $make(Sex,'Ken Chandler',Male) -? eval $make('Birth Date','Maggie Horton','04/07/43') -? let number = $made('Birth Date',$any,$any) -? / * #number * 2 -? let test = $made($any,'Ken Chandler',$any) -? / * #test * 2 -? let all = $made($any,$any,$any) -? / * #all * 3 -?
$UNMAKE is a three-argument function that unmakes the triple(s) specified on the basis of generic or specific criteria. It returns the integer number of triples unmade.
The forms for $UNMAKE are:
LET integer = $UNMAKE(attribute,object,value) EVAL $UNMAKE(attribute,object,value)
where attribute, object, and/or value can be replaced by $ANY. The command
EVAL $UNMAKE($ANY,$ANY,$ANY)
unmakes all triples.
Note that $UNMAKE is somewhat similar to $MAKE in that it does not report an error if its action has already been done. That is, $UNMAKE simply guarantees that the triple(s) does not exist; if you try to UNMAKE a triple that does not exist, no error is reported, but a zero integer value is returned.
$UNMAKETRIPLE is a function with a single argument that is the address of the triple to be unmade. It allows you to unmake a triple at a specified address without knowing or specifying any of its component parts.
The forms of the function are:
LET integer = $UNMAKETRIPLE(address) EVAL $UNMAKETRIPLE(address)
where the address is usually a variable that has been assigned a triple address, and "integer" is the "use-count" of the triple.
For example:
-? let t1 = $make(a,b,c) -? let t2 = $make(a,b,c) -? let t3 = #t1 -? let count = $unmaketriple(#t1) -? / * #count * 3 -? let t2 = $zap -? let count = $unmaketriple(#t1) -? / * #count * 2
Note that the "use-count" of a triple, returned by $UNMAKETRIPLE, indicates the number of variables that were directly assigned the address of the triple (T1, T2, and T3 in the above example). When these references to the triple are erased (by $ZAP), then the use-count is decremented. Until the use-count of a triple is 0, the triple continues to exist -- that is, it may be referenced by functions that have the address of the triple as their argument -- but it does not exist in its own right and is unavailable to triples functions that require specification of components, such as $MADE.
$LOOKUP is a three-agrument function that returns the address of the first triple that satisfies the generic or specific criteria supplied as the arguments of the function.
The form of the function is:
LET address = $LOOKUP(attribute,object,value)
where attribute, object, and/or value can be specified as $ANY to indicate generic criteria.
If $LOOKUP can not find any triple, $TRINULL is returned. Therefore, if #address = $trinull then * no triple was found.
Note that criteria must be specified in the exact form used when the triple was made; e.g., criteria are case-sensitive.
$ATTRIBUTE, $OBJECT, and $VALUE are functions with a single argument that specifies the address of a triple and returns the requested component of the triple at that address. These functions are used to isolate one component of a triple given the triple's address.
The form of each of the functions is:
LET attribute = $ATTRIBUTE(address) LET object = $OBJECT(address) LET value = $VALUE(address)
where the address is obtained from functions such as $MAKE and $LOOKUP for example.
For example:
-? let t1 = $make('Birth Date','Maggie Horton','04/07/43') -? let att = $attribute(#t1) -? / * #att * Birth Date -?
Note that the type of the data returned from these functions varies depending on the type of the component when the triple was made. For example:
-? let t1 = $make('Birth Date','Maggie Horton',$datein('04/07/43')) -? let type = $type($value(#t1)) -? / * #type * HEX -?
If the attribute, object, and/or value used to make the triple was itself a triple, then $ATTRIBUTE, $OBJECT, and/or $VALUE return the address of that triple; the $ATTRIBUTE, $OBJECT, and/or $VALUE functions can then be reissued using the new address to retrieve the specified component. For example:
-? let t1 = $make( $make(a,b,c), d, e) -? let obj = $object( $attribute(#t1) ) -? / * #obj * b -?
If a triple was made using variables for one or more of its components, then the triple is an association of the values of those variables when the triple was made; the value of the component will not change as the value of the variable used for that component changes. For example:
-? let variable = 'first value' -? let t1 = $make(a,b,#variable) -? let variable = 'second value' -? let value = $value(#t1) -? / * #value * first value -?
But note that the value of a component can be the name of a variable, rather than the value of the variable. For example:
-? let variable = 'first value' -? let t1 = $make(a,b,'#variable') -? let variable = 'second value' -? let value = $evaluate($value(#t1)) -? / * #value * second value -?
"Groups" are an association of any number of entities, all of which must be triples. $GROUP is a three-argument function that makes a group out of all triples that qualify on the basis of generic criteria, and returns the "address" of the group.
The form of the function is:
LET address = $GROUP(attribute,object,value)
where attribute, object, and/or value can be replaced by $ANY. The address of the group is important, since it is necessary in all subsequent functions that operate on the group.
If $GROUP can not find any triples to form a group, then $GRPNULL is returned. Therefore, if #address = $grpnull then * no group was formed.
A group is destroyed with the $ZAP function used on the variable containing the group's address; only the grouping itself is eliminated, not the individual triples that made up the group.
For example:
-? let sample = $group('Birth Date',$any,'04/07/43') -? let sample = $zap
Note that the "address" returned from the $GROUP function can be tested like a flag variable to determine if a group was created.
For example:
-? let groupaddress = $group('Birth Date',$any,'02/02/02') -? if ~#groupaddress then * No one born on 2/2/2! * No one born on 2/2/2! -? / * #groupaddress * #groupaddress -?
Note that, like a triple's address, the address of a group cannot be displayed with a * command. The address of a group, when stored, must be stored into a variable of TYPE=DYNAMIC; the variable becomes TYPE=GRP:
-? let groupaddress = $group('Birth Date',$any,'02/02/02') -? let temp = $type(#groupaddress) -? / * #temp * GRP -?
Addresses of triples and groups cannot be compared to non-triple and non-group addresses in IF statements:
-? let groupaddress = $group('Birth Date',$any,'02/02/02') -? if #groupaddress = 0 then * No one born on 2/2/2! -CONVERSION ERROR: STR TO GRP , VALUE = '0' -NOT LEGAL COMMAND -?
$GROUPSIZE is a function with a single argument of the address of a group; it returns the number of the triples in the group.
The form of the function is:
LET integer = $GROUPSIZE(address)
where the address of the group is obtained from the $GROUP function.
For example:
-? eval $make('Birth Date','Ken Chandler','07/27/45') -? eval $make(Sex,'Ken Chandler',Male) -? let g1 = $groupsize( $group($any,'Ken Chandler',$any) ) -? / * #g1 * 2 -?
$GROUPSORT is a two-argument function that specifies the address of the group and the component to be sorted on, and returns the address of a new (sorted) group.
The form of the function is:
LET address = $GROUPSORT(address,{ATTRIBUTE|OBJECT|VALUE})
where the words "ATTRIBUTE", "OBJECT" or "VALUE" can be abbreviated to one or more characters.
The "address" value is frequently needed when a group is created and sorted in a single command; the address is TYPE=GRP. For example:
-? let g1 = $groupsort( $group(a,$any,c), object)
If the address returned is "0", then no group was created. The sort of a group is always done in ascending character (unsigned) sequence. This gives the following sequence, for example: $INT(0), $INT(1), $INT(2), ... '.', 'a', 'b', 'c', ... 'A', 'B', 'C', '0', '1', '2', $NEG(2), $NEG(1). Note that the sorting of a group does not maintain the input order of the triples in the group; that is, first in, first out is not guaranteed.
$GROUPELEMENT is a function whose two arguments specify the address of a group and the ordinal number of a member (or "element") in the group. It returns the address of the triple at that position in the group.
The form of the function is:
LET triple-address = $GROUPELEMENT(group-address,element-ordinal)
where the "element-ordinal" is an integer string ranging from 1 (indicating the first member of the group) to the size of the group.
For example:
-? eval $make('Birth Date','Ken Chandler',$datein('7/27/45')) -? eval $make('Birth Date','Maggie Horton',$datein('4/07/43')) -? let g1 = $group('Birth Date',$any,$any) -? let g1 = $groupsort(#g1,value) -? let member1object = $object($groupelement(#g1,1)) -? let member1value = $value ($groupelement(#g1,1)) -? let member2object = $object($groupelement(#g1,2)) -? let member2value = $value ($groupelement(#g1,2)) -? / * #member1object #member1value * Maggie Horton 19430407 -? / * #member2object #member2value * Ken Chandler 19450727 -?
The CLEAR TRIPLES command unmakes all triples; it is equivalent to $UNMAKE($ANY,$ANY,$ANY). [See 7A.5.] A subsequent $LOOKUP($ANY,$ANY,$ANY) [See 7A.7.] will find no triples that exist in their own right (though references to triples' addresses may still exist via variable references).
The SHOW TRIPLES command lists all triples made in the session, including triples that have been unmade but are still referenced (i.e., the "use-count" [See 7A.6.] of the triple is non-zero). The display from the command shows the address of the triple, followed by its attribute, object, and value components (any or all of which may themselves in turn be an address).
For example:
-? show triples %7553432: a, b, -5 X7552768: a, %7553432, 4 %7554448: a, b, c -?
The "X" address prefix indicates that the triple has been unmade -- i.e., no longer exists in its own right and can no longer be located by $LOOKUP -- but continues to exist because it is still referenced. If all the variables assigned the triple's address are deleted via the $ZAP function, then the triple ceases to exist at all, and SHOW TRIPLES will not display it.
The "%" address prefix indicates that the triple exists in its own right -- i.e., has not been unmade -- and may be referenced. If the "%" prefix appears on one of the components of a triple (as in the second line of the display above), then that component itself is a triple.
The SHOW DYNAMIC VARIABLES command [See 8.2.] can be used to display variables whose value is the address of a triple. Usually, a dynamic variable whose value begins with a "%" is a variable containing the address of a triple. The existence of groups is shown by the SHOW DYNAMIC VARIABLES command; groups themselves are not displayed by SHOW TRIPLES.
To a greater extent than other functions in SPIRES, the functions associated with triples only accomplish useful work when they are used in combination. Thus, it is important to understand how combinations of triples functions are usually used.
The types of applications in which triples are useful are usually those in which associations must be made between items whose domain (i.e., range of values) is not predictable or well-bounded. Triples are also useful in applictions where associations must be formed among more than two things, and where the typical approach of using large and/or multiple arrays would be cumbersome because of core or other limitations.
The typical use of making associations while processing data then reporting on the associations made at the end of processing can generally use the following recipe:
# Function Process 1 $UNMAKE Use $ANY for all components to unmake all triples 2 $MAKE Use to associate two or more items into a triple, and repeat use of the $MAKE function until all required associations have been made 3 $GROUP Associate generically-selected (using $ANY) triples into a group; subsequent manipulation is of the group 4 $GROUPSORT Sort the group by one of its components 5 $GROUPSIZE Determine the size of the group prior to processing individual members ("elements") of the group 6 $GROUPELEMENT Use to obtain the address of each triple in the group, repeating 6/10 until all elements are processed 7 $ATTRIBUTE Obtain the Attribute component of the nth triple in the group 8 $OBJECT Obtain the Object component of the nth triple in the group 9 $VALUE Obtain the Value component of the nth triple in the group 10 $UNMAKETRIPLE Use to unmake the nth triple in the group, if necessary, then increment "n" and process the next element in the group by returning to #6 above. 11 $ZAP Erase the grouping 12 $UNMAKE Use $ANY for all components to unmake all triples.
A second useful example of a programming technique using triples involves the association of several components into a single unit. Typically, this kind of processing is done with several arrays, having all of their occurrence-counters matching each other. But this approach may require large arrays, with careful programming to keep them synchronized, and several statements to set and examine values in each of the arrays. Using triples, the existence of a unit can be created with a single command; the existence of a specific unit, or of a class of units, can then be detected at a later point in the program with a single command.
This type of processing has been used to handle error message processing in full-screen applications where complex screens have multiple levels of error messages, and the screen design itself is subject to change. When an error is detected, the controlling format can make a triple from components that are the row and column numbers, the error level, and the message number; if no error is detected, then the triple would be unconditionally unmade.
A label group in this format might look as follows:
LABEL = SALARY; GETDATA; START = 5,6; LENGTH = 7; UPROC = IF $PROCERR THEN EVAL $MAKE(ERR,$MAKE(5,2,31),S); UPROC = ELSE EVAL $UNMAKE(ERR,$MAKE(5,2,31),$ANY); COMMENTS = ROW 5, COLUMN 2 IS WHERE THE ERROR MESSAGE WILL BE; COMMENTS = DISLAYED. THE 'S' AND '31' ARE THE ERROR LEVEL AND; COMMENTS = ERROR NUMBER, RESPECTIVELY.; . . . other statements in the label-group . . .
A frame dedicated to error-message display can have a single label-group executed repeatedly to process all error messages:
LABEL = ERROR.GROUPING; UPROC = LET GROUPADD = $GROUP(ERR,$ANY,$ANY); UPROC = IF ~#GROUPADD THEN RETURN; UPROC = LET NUMBEROFERRORS = $GROUPSIZE(#GROUPADD); UPROC = LET MEMBER = 1; COMMENTS = IF THERE WERE NO TRIPLES TO MAKE A GROUP OUT OF,; COMMENTS = THEN RETURN AND PUT OUT NO ERROR MESSAGES.; COMMENTS = NOTE THAT GROUPADD AND TRIADD VARIABLES ARE ; COMMENTS = DECLARED AS 'TYPE=DYNAMIC' IN THE VGROUP. ; LABEL = ERROR.DISPLAY; UPROC = LET TRIADD = $GROUPELEMENT(#GROUPADD,#MEMBER); UPROC = - The row is the attribute of a 'nested' triple: ; UPROC = SET CROW = $ATTRIBUTE($OBJECT(#TRIADD)); UPROC = - The column is the object of a 'nested' triple: ; UPROC = SET CCOL = $OBJECT($OBJECT(#TRIADD)); UPROC = - The error level is the object of the non-nested triple,; UPROC = - and the error message number is the value of the; UPROC = - non-nested triple. Assemble these into $CVAL to; UPROC = - display them at $CROW and $CCOL: ; UPROC = SET CVAL = $VALUE(#TRIADD) || $VALUE($OBJECT(#TRIADD)); PUTDATA; LABEL = NEXT.MEMBER; UPROC = IF #MEMBER = #NUMBEROFERRORS THEN RETURN; UPROC = LET MEMBER = #MEMBER + 1; UPROC = GOTO ERROR.DISPLAY;
Subsequent processing can force the user to correct all serious-level errors, and can avoid renotifying the user of warning-level errors by eliminating the triples that indicate they exist:
UPROC = EVAL $UNMAKE(ERR,$ANY,W);
Reprompt processing will continue until no serious errors are detected:
UPROC = IF $MADE(ERR,$ANY,S) : REPROMPT;
There are several capabilities available to facilitate protocol debugging. These are diagnostic messages, display of variable values, and command tracing. Each is discussed briefly below.
The list below shows some (by no means all) of the diagnostic messages issued by the system when protocol errors are encountered. In general, when an error occurs, the execution is broken back to COMMAND mode, and the "-Continue XEQ?" prompt is displayed. For instance:
-No such subfile -Continue XEQ?
Some common diagnostics are:
-'++label.name' not found [if a jump specifies a non-existent label] -Variable value for variablename incorrect type -Cannot 'XEQ FROM' this subfile -Allowed only in XEQ -Nothing to continue -Nothing to break -Bad length or value -Nothing to XEQ -Not enough core for XEQ -No such variable -Unrecognized: [text of statement whose syntax is faulty] -Not a legal or complete command -Conversion error: type to type, value = constant
If you answer 'no' to the 'Continue XEQ?' prompt, you are returned one level, usually back to command mode, signified by the '->' prompt. However, if you answer 'break' to the 'Continue XEQ?' prompt, you cause a temporary execution break in the protocol -- you can later continue executing it from the point where you left off. [See 2.4.]
In either case, you can display any variable known to the system by using the command forms:
-> Show Eval #Variablename or -> Show Eval $system-variablename
Answering 'break' places you in a BREAK XEQ state, from which the command
-> Show Eval $LastXeq
can be issued, displaying the protocol statement that the system was trying to execute when an execution break was caused; it is usually the statement that caused the 'Continue XEQ?' prompt.
In addition, while in BREAK XEQ state, you can step your way, statement by statement, through a protocol stream by using the command
X-> XEQ NEXT
You may issue this command repeatedly.
The content of static or dynamic variable groups may be displayed by using the commands
SHOW STATIC VARIABLES [BRIEF] or SHOW STAVAR [BRIEF] SHOW DYNAMIC VARIABLES or SHOW DYNVAR SHOW STATIC VARIABLES OF vgroupname
The BRIEF option on the SHOW STATIC VARIABLES command provides the same information as the SHOW STATIC VARIABLES command except that a series of zero-valued or null-valued variables in an array is condensed for display to only the first and last in the series, separated by a set of dashes to indicate those variables left off the display.
The following command shows the names of all allocated VGROUPS.
SHOW ALLOCATED or SHOW ALL
Having displayed the contents of appropriate variables, you may modify them by using LET commands. After this, if further execution is possible, you may issue the command:
X-> continue xeq
to resume processing at the command where the error occurred.
A programmer may want to free the memory space held by a specific user-declared variable to prevent values of variables in one protocol from being used as starting values of the same variable in another protocol or simply to return memory to the computer. Once memory is allocated for dynamic variables, this space remains in use until the SPIRES session ends. The programmer may free the space taken by individual dynamic variables by using the $ZAP function:
LET M = $ZAP
clears the dynamic variable M from memory. Global clears are available for dynamic and static variables:
CLEAR DYNAMIC VARIABLES or CLEAR DYNVAR DEALLOCATE vgroupname
The global dynamic clear should be used with care, since a user may have his own dynamic variables in core when a protocol issues a global clear, thus erasing all of the user's dynamic variables in core.
The current position in a hierarchy of nested XEQ protocols can be displayed with a command of the form:
SHOW XEQ STACK
For debugging protocols, SPIRES provides two different but related tracing facilities that show you the progress (or lack thereof) of a protocol as it executes:
Since SET ECHO was described earlier in this manual, this section will introduce SET XTRACE. [See 8.4.1.] Additionally, a logging facility, TLOG, that lets you send the SET ECHO data or SET XTRACE data or both to a log (and away from the terminal screen) will be discussed. [See 8.4.2.]
SET XTRACE is the basic command for activating protocol tracing, where SPIRES displays environmental information about the protocol being executed. Depending on the options selected, SPIRES can show you when the protocol or any nested procs or protocols begin and end execution, the new values of any or specific user variables when they change, and what label group any JUMP or GOTO command invoked. Additionally, when you are debugging complex protocols that invoke other protocols, you can ask for tracing only for specific protocols.
There are four flavors of the SET XTRACE command, depending on which options you want to use. You may issue multiple SET XTRACE commands to achieve particular combinations.
The basic command is:
SET XTRACE
With this in effect, SPIRES will display an "-Entering: <name>" and "-Leaving: <name>" line for each protocol and proc (invoked by the XEQ PROC command) as execution of it begins and ends. The display will be indented appropriately to indicate the nesting of the protocols and procedures. Procs will be distinguishable from protocols by the "++" prefix (i.e., the label indicator) in front of their names.
To request the basic XTRACE information plus "jump" information, use:
SET XTRACE JUMP
Each time a JUMP or GOTO command is executed, SPIRES will display a "-Jump to <labelname>" line.
To request the basic XTRACE information plus a trace line whenever a specific or any user variable is changed, add the VARIABLES option:
SET XTRACE VARIABLES [vars list | - vars list | + vars list]
If no "vars list" option is specified, SPIRES will display a "varname = value" line each time any user variable changes during protocol execution. For example, if variable #Redisplay is set to "0", the line "Redisplay = 0" will appear in the trace information.
But you can ask for only specific variables to be displayed by naming them in the optional variables list -- note that you omit the preceding pound sign when specifying them here. You can also begin by adding an "exclusion list", listing the variables you don't want to see by preceding them with a minus sign; in other words, all user variables except those named in the exclusion list will be included in the trace list.
Once you have set the VARIABLES option, you can change the list of variables by reissuing the SET XTRACE command with an exclusion list (preceded by a minus sign) or an inclusion list (preceded by a plus sign). The named variables will then be removed or added from the tracing list as appropriate.
The fourth flavor of SET XTRACE requests the basic tracing and lets you limit the tracing to only specific protocols being executed:
SET XTRACE PROTOCOLS {protocols | - protocols | + protocols}
where "protocols" is a list of protocols that you want to include or exclude from the tracing. The first time you use the PROTOCOLS option in a given XTRACE session, you specify either an inclusion list ("protocols"), the protocols for which you want tracing, or an exclusion list ("- protocols"), the protocols for which you don't want tracing. Later, you can change the list of protocols being traced by adding new ones ("+ protocols") or removing others ("- protocols").
A convenient shortcut for setting all the options at once is:
SET XTRACE ALL
This is exactly the same as issuing the commands SET XTRACE JUMP, SET XTRACE VARIABLES and SET XTRACE PROTOCOLS in a row.
Turning off the tracing is as simple as:
CLEAR XTRACE [ALL]
CLEAR XTRACE and CLEAR XTRACE ALL are equivalent, turning off all the tracing. However, you can turn off the individual options as well:
CLEAR XTRACE [JUMP | VARIABLES | PROTOCOLS]
In the example below, protocol AAA calls protocol BBB, which in turn calls proc CCC, which calls protocol DDD.
-> set xtrace variable status -> set xtrace jump -> ..aaa -Entering: AAA -Entering: BBB -Jump to ASK.STATUS STATUS = OK -Entering: ++CCC -Entering: DDD STATUS = EXIT -Leaving: DDD -Leaving: ++CCC STATUS = QUIT -Leaving: BBB -Leaving: AAA ->
The example shows the basic tracing data ("Entering" and "Leaving" lines, with information shown for the variable STATUS and for JUMPs as requested with the VARIABLE and JUMP options. Continuing this example, the tracing will look different when limited only to a single protocol:
-> set xtrace protocol BBB -> ..aaa -Entering: BBB -Jump to ASK.STATUS STATUS = OK -Leaving: BBB ->
SET XTRACE is often used in combination with SET ECHO, which displays the protocol commands as they are executed [See 5.6.] and with SET TLOG, which directs the tracing data to a temporary log file, which can be examined with the SHOW TLOG DATA command. [See 8.4.2.]
You can see what SET XTRACE commands are in effect with the SHOW XTRACE command:
[IN ACTIVE] SHOW XTRACE
Since SHOW XTRACE lists the actual commands, you can use the IN ACTIVE command to put them into your active file, where you can modify them and then re-execute them with the XEQ command. [See 2.1.] The $XTRACE variable can also be checked to determine what xtrace options are in effect.
By default, the three commands that produce tracing information for protocols and formats, SET ECHO, SET XTRACE and SET FTRACE, display their information to the terminal screen while the protocol or format is executing. Sometimes, however, it's more convenient to send the tracing data elsewhere, both so that it can be captured and studied, and so that it doesn't get mixed up with whatever the format or protocol is sending to the terminal screen. The "somewhere" that SPIRES provides is called the "trace log", or "tlog" for short.
When you use the SET TLOG command to activate the trace log, SPIRES will send the trace output and, optionally, any error messages to the log. Later, you use the SHOW TLOG DATA command, perhaps with the IN ACTIVE prefix, to examine the contents of the trace log.
The syntax of the SET TLOG command is:
SET TLOG [MSG|NOMSG]
By default, system messages will go to the trace log (at message-level 3), regardless of the SET MESSAGES setting. (Hence, messages will still go to the terminal screen as well, unless they've been turned off there.) If you don't want the system messages included in the log, add the NOMSG option. To change the MSG or NOMSG setting later, simply re-issue the SET TLOG command with the appropriate option.
To see the contents of the trace log, use the SHOW TLOG DATA command:
[IN ACTIVE...] SHOW TLOG [DATA] [MSG|NOMSG] [PURGE]
Strictly speaking, the word DATA is optional, but we will refer to the command as SHOW TLOG DATA for clarity.
The MSG option tells SPIRES to display only the system-messages data and not the other tracing information; the NOMSG option displays only the echo, xtrace or ftrace information that has been saved in the log, without any system messages. If neither MSG nor NOMSG is specified, the entire contents of the log are displayed. (Note that you cannot see any system-messages data if you have turned off its logging with the SET TLOG NOMSG command.)
The PURGE option can be added to discard the current contents of the trace log when the display is completed; all the contents will be discarded, even if MSG or NOMSG is specified.
If you want to put the log into your active file, use the standard IN ACTIVE prefix, with or without its options such as CLEAR or KEEP.
You can also clear the contents of the trace log with the command:
CLEAR TLOG DATA
This command in effect discards the current trace log and prepares to start a new one. This has the same effect as the PURGE option on the SHOW TLOG DATA command.
On the other hand, to end the logging altogether:
CLEAR TLOG
This command not only discards the current trace log, but turns off the logging itself.
To direct custom messages to the log, try using the $ISSUEMSG function [See 7.4.8a.] as in this example:
IF $XTRACE THEN EVAL $ISSUEMSG('* Start of Problem Area *', W)
For most tracing situations, $ISSUEMSG is preferable to using the "*" command, which will only appear on the terminal screen and never as part of the trace log.
SPIRES can keep a log of error messages that arise, which has some similarities to the trace log described in the previous section. [See 8.4.2.] The error log is useful in situations where you may receive multiple errors -- for example, where a record is being added to a subfile and multiple Inproc errors occur. Without the log, the protocol controlling the transaction would have direct access only to information about the last error encountered, not all of them. But with the error log (or "elog") turned on, the protocol can use the $GETELOG function to retrieve all the error messages individually.
The commands you use to initialize and work with the error log are similar to those for the trace log; however, the two logs are completely separate.
When you use the SET ELOG command to activate the error log, SPIRES will send any subsequent serious or warning error messages to the log, including serious or warning messages created by the $ISSUEMSG function. Informational and unconditional messages do not go into the error log. (Unconditional messages always go to the assigned message area. See the explanation for $ISSUEMSG for a description of unconditional messages.)
The syntax of the SET ELOG command, which activates and initializes error logging, is simply:
SET ELOG
To see the contents of the error log at any time, use the SHOW ELOG command:
[IN ACTIVE...] SHOW ELOG [PURGE]
The PURGE option can be added to discard the current contents of the error log when the display is completed.
To put the log into your active file, use the standard IN ACTIVE prefix, with or without its options such as CLEAR or KEEP.
You can also clear the contents of the error log with the command:
CLEAR ELOG DATA
This command in effect discards the current error log and prepares to start a new one. This has the same effect as the PURGE option on the SHOW ELOG command.
On the other hand, to end the logging altogether:
CLEAR ELOG
This command not only discards the current error log, but turns off the logging itself.
While you can see the entire log with the SHOW ELOG command, programs more commonly use the $GETELOG function to access its data. This function can retrieve not only the individual pieces of the log shown with the SHOW ELOG command but also other related data stored in the log that doesn't appear under SHOW ELOG.
For information about the $GETELOG function, see chapter 7, or online, EXPLAIN $GETELOG FUNCTION.
Two other variables associated with error logging are $ELOGSCNT and $ELOGWCNT, which count the number of Serious and Warning errors currently captured in the log, respectively.
You can also test each of your protocol statements individually, by typing the command at the terminal. [This assumes that the command is allowed at command level -- some commands such as JUMP are not.] Testing commands singly can be easier for quick debugging than adding a protocol to your protocols file or issuing the XEQ command for an entire active file.
For instance, you can reformulate a syntactically faulty command on the spot, until it executes correctly:
-> SET WDST (5) IN 5/LAST -Unrecognized: IN 5/LAST -> SET WDST (5) 5/LAST ->
The return of the command prompt ('->') signals that SPIRES found the syntax of the rewritten command acceptable.
The PERFORM BUILD PROTOCOLS command is an ideal way to generate a standard protocol file. After you issue the command, you are asked questions about the file's name and the accounts you wish to give access to the file. Once you have answered these questions, the utility generates a file definition for your protocols file, adds the definition to the FILEDEF subfile, and compiles the file for you.
The resulting file automatically uses the $PROTOCOL format which gives you a convenient method to search the file by subject and to print your protocol records using the PERFORM PRINT command.
PERFORM BUILD PROTOCOLS is described in detail along with other PERFORM utilities in "SPIRES Technical Notes". Below is a sample session in which user GQ.JPR builds a protocol file with file name GQ.JPR.TESTPROTOS. (The subfile name is TEST PROTOS.) The part of the session that compiles the file definition occurs automatically without the user having to issue those commands:
-> PERFORM BUILD PROTOCOLS -Do you need instructions? NO -What subfile name would you like (return=JPR PROTOCOLS)? test protos -What file name (return=*PROTOCOLS)? testprotos -Other accounts permitted to read and update? -Accounts permitted to read only? GQ Record GQ.JPR.TESTPROTOS added to FILEDEF. -COMPILE GQ.JPR.TESTPROTOS <--(PERFORM BUILD PROTOCOLS -File definition compiled compiles the file for you automatically) ->
The generated protocol file uses the gen-file format $PROTOCOL, which conveniently handles both display requests and input requests such as ADD, UPDATE or ADDUPDATE, and adds formatting features to protocols printed out with the PERFORM PRINT command. See the manual "SPIRES Technical Notes" for full information about the $PROTOCOL format and how it interacts with PERFORM PRINT.
The PUBLIC PROTOCOLS file, although less often used today than in the past, contains various protocols that are available for "public execution," so to speak. A current list of protocols in the PUBLIC PROTOCOLS file, as well as a brief description of their function, can be obtained from the SPIRES file called MASTERLIST. To get such a list, execute the following SPIRES commands:
-> select masterlist -> find type protocol -> type
If you would like to see the protocol commands that make up any protocol in the PUBLIC PROTOCOLS file, you may
-> select public protocols -> display protocol.name
If you would like an off-line listing of any of the public protocols,
-> select public protocols -> set xeq -> ..list.protocol
You will then be prompted for the name of the protocol you want to be listed offline.
The PFORMAT command (which is actually a WYLBUR command) reformats protocols code in your active file, providing a standard indention style. PFORMAT also checks for unmatched pairs of BEGINBLOCK...ENDBLOCK, REPEAT...UNTIL, and WHILE...ENDWHILE statements.
Labels begin in column 1, and code starts in column 3. Block structures are indicated by further indention (in increments of 3, by default) based on the depth of nesting. Command lines that are continued with the "\" character are also indented 3 spaces by default. An INDENT option lets you control the number of spaces used for this second level of indention. The INDENT value can be any number from 2 -- 16:
PFORMAT [INDENT = n]
Comments will stay in column 1 if they start there; otherwise they are indented the same as command lines.
DECLARE statements in Prism protocols start in column 1, with subsequent options (such as ON EVENT or ON COMMAND declarations) starting in column 3.
The "/" preparse character is relocated to the column preceding the start of the command to which it applies. The "!" noecho character is relocated to column 1.
Here is an example of the standard formatting supplied by PFORMAT:
* form/mod payment - Written by John Doe declare screen INPUT on event choice, in zone STATUS, proc CHOOSE.STATUS on command CANCEL, proc CHECK.CANCEL ++LABEL if #status = 'S' then begin let occ = 1 let maxocc = 20 while #occ <= #maxocc let key = $acct || #occ /stack #key let occ = #occ + 1 endwhile sequence ID, NAME if $no then set status 'STOP' let count = $stack endblock else begin perform prism screen GET.STATUS, ok, send \ title='Status information' if $lastcmd = 'SEND' then BEGIN - early SEND; mark record as incomplete let routestatus = $false let itemstatus = 'X' using CLOSE, merge endblock endblock return
Application developers are urged to compile their frequently used and/or complex protocols. This chapter focuses on the procedures for compiling protocols and using the compiled versions. Most of the chapter describes and refers to a method of compilation which compiles the protocol directly from the SPIRES file in which it is stored. An older procedure, described at the end of this chapter, compiles the protocol from a meta-data record stored in the SYS PROTO subfile; that procedure, called the SYS PROTO method, is no longer recommended, but is described in this chapter for compatibility with older applications. This chapter ends with a section about converting from the SYS PROTO method to the newer one.
Compiling a protocol provides you with two very solid benefits:
- the compiling process provides additional error checking
- most protocols execute more efficiently in compiled form
Thus, though you are never required to compile a protocol (as you are required to compile a format or file definition) you will nearly always benefit from compiling a protocol that supports a production application in Prism or SPIRES.
At what stage should a protocol be compiled? One guideline: in the early stages of a protocol's development, when it is being tested and frequently changed, you may find it easier to test in its uncompiled form. Later, when the protocol moves closer to production, you will probably want to compile it for the sake of efficiency.
There are two methods for compiling a protocol. The older way, called the SYS PROTO method, is described at the end of this chapter. [See 10.8.] The newer method, described in the first few sections of this chapter, is the recommended way; it offers several advantages over the older way, including:
- No SYS PROTO record is needed, which helps reduce the number of pieces of code in your application.
- The local variables you need in a protocol are defined right in the protocol itself, rather than in a separate SYS PROTO record.
- There are fewer chances for problems with variable resolution. All variables in a protocol must be defined in a local or global vgroup under the new method; in the old way, a variable whose name didn't match any variables in allocated vgroups (usually because of a typo; it was seldom intentional) would get created dynamically rather than cause an error.
Unless you are wedded to the old method because conversion of your application to the new method would be too laborious, you should use the new method. [See 10.9 for information about converting from the SYS PROTO method to the new form.]
Before taking the steps below, you should have created a protocols subfile of your own, probably using PERFORM BUILD PROTOCOLS. [See 9.1.] The protocol you wish to compile should be a record in this subfile and it should already be largely tested and debugged. [See 8.]
Assuming the above conditions have been met, take the following steps to compile a protocol:
- Step 1: Select the subfile containing the protocol.
- Step 2: Issue the COMPILE command followed by the protocol name (the key of the protocols subfile record):
COMPILE protocol-name
If you are recompiling a protocol that you compiled previously, use the RECOMPILE command instead:
RECOMPILE protocol-name
This step is discussed in more detail in the next section. [See 10.2.]
To use the compiled protocol, you need to tell SPIRES what subfile contains it (with the SET COMPXEQ command), and then you start the protocol's execution with the "XEQ FROM protocol" or "..protocol" command. [See 10.3.]
The example below shows the user compiling a protocol named LOOK.FOR.CAT.LADIES that is stored in a protocol subfile named CAT NETWORK PROTOS.
-> select cat network protos
Note: This protocols subfile must have been built using the $PROTOCOL record-type. [See 10.9.]
Use the COMPILE command to compile the SYS PROTO record:
-> compile look.for.cat.ladies -Compiled protocol: LOOK.FOR.CAT.LADIES -Protocol compile finished ->
Roughly speaking, compiling a protocol causes a second version of the protocol, somewhat rewritten for efficiency, to be stored in a system file. But the original, uncompiled protocol continues to sit in your protocols subfile unchanged.
If you select the protocols subfile for COMPXEQ, then SPIRES will execute the compiled version of the protocol when you request it. However, if you select the protocols subfile for XEQ, then SPIRES will execute the uncompiled version of the protocol when you request it. If you have the subfile set for both XEQ and COMPXEQ, then SPIRES will use the uncompiled version. [See 2.2, 10.3, 10.6.]
In addition to describing compilation in more detail, this chapter demonstrates some other features of compiled protocols:
- Efficient coding strategies for compiled protocols [See 10.4.]
- How variables communicate between different vgroups [See 10.5.]
- Protocol execution order when compiled and non-compiled protocols are both set for execution [See 10.6.]
These sections will provide you with a more complete picture of compiled protocols.
In order to compile a protocol, you must first select the protocols subfile where the protocol is stored. [The protocols subfile must have been built using the $PROTOCOL record-type. Use the PERFORM BUILD PROTOCOLS command to create one. [See 9.1 Almost all protocols subfiles except the oldest ones are built this way., 10.9.]]
Then issue the COMPILE command:
-> select my protocols -> compile anyname
If the protocol has been compiled before, use the RECOMPILE command instead of the COMPILE command. If the protocol has any blanks in its name, put the name in quotation marks or apostrophes on the command:
-> compile 'add new record'
To obtain statistics on the compiled protocol, you can append the STATISTICS option to the COMPILE or RECOMPILE command. [See 10.7.]
If all goes well with the compilation, you will receive the following informational messages:
-Compiled protocol: protocol-name -Protocol compile finished
At that point, you can issue the SET COMPXEQ command and go on to execute the compiled protocol. [See 10.3.]
There's always a chance, of course, that the protocol will fail to compile or will give rise to a warning message after you issue the COMPILE or RECOMPILE command. Make changes to the protocol as needed, update it in the protocols subfile, and try to compile it again.
If you are not sure what the error message means, try using the EXPLAIN command, or else check the appendix in the back of this manual.
The following warning message indicates that one of the internal tables that SPIRES creates is close to its maximum size:
-Warning: large entries in Protocol Statistics for <protocol-name>
See the later section on statistics for details. [See 10.7.]
Other possible causes of errors in compilation include duplicate label names and undefined label names (e.g., if the protocol includes a "JUMP label.name" statement but the "label.name" does not occur somewhere in the protocol).
In addition, the protocol would be rejected if it contained too many labels for the compiler's table, or if the protocol as a whole were too large to be compiled. In the latter case, you might need to break the protocol into parts, perhaps using the "IN protocol.name" suffix to the XEQ PROC command in order to coordinate the separate parts. [See 3.2.1.]
When you want to get rid of the compiled form of a protocol, use the ZAP PROTOCOL command:
ZAP PROTOCOL protocol-name [SOURCE] OF protocol-subfile
where "protocol-name" is the name of the protocol, and "protocol-subfile" is the name of the subfile where it is stored.
If you add the SOURCE option, SPIRES will discard not only the compiled version (which is actually stored in a system subfile called COMP PROTO) but also the source record for the protocol in the named protocol subfile.
So, for instance, to get rid of the compiled and source version of the protocol TEST.ADD.ADDRESS from the subfile JNK PROTOCOLS, you could issue the command:
-> zap protocol test.add.address source of jnk protocols
In order to use pre-compiled protocols, you need only issue a SET COMPXEQ command instead of the SET XEQ command. [See 2.2 for SET XEQ.]
A session using compiled protocols might look like this:
COMMAND> call spires -> set compxeq test protos -> ..list.filedef -> show compxeq -'TEST PROTOS' set
To execute your own compiled protocols, SELECT your protocol subfile and issue the SET COMPXEQ command, or issue the command "SET COMPXEQ subfile-name", where "subfile-name" is the name of the subfile you want to be your COMPXEQ subfile.
You can set "secondary" COMPXEQ subfiles as well:
SET COMPXEQ {+|-} [subfile-name]
This form of the command can only be issued if you have previously set a "primary" COMPXEQ subfile. Using the "+" operator will add another COMPXEQ subfile; using the "-" operator will remove the named subfile from the COMPXEQ list. You cannot use the "-" operator to eliminate the primary COMPXEQ subfile. (Note that "+" and "-" must be surrounded by one or more blanks.)
If no subfile is named in the command, the currently selected subfile (or currently attached goal record-type if no subfile is selected) will be added or deleted from the list of COMPXEQ subfiles.
If you have set one or more secondary COMPXEQ subfiles, when you issue an "XEQ FROM" or "..protocol" command, SPIRES will first look for the protocol in the XEQ subfile (if there is one). If it doesn't find the named protocol in the XEQ subfile, it looks in the primary COMPXEQ subfile and then in each of the secondary COMPXEQ subfiles, in the order in which they were set. [See 10.6.] An error condition is raised if the protocol cannot be found in any of these subfiles.
To eliminate all secondary COMPXEQ subfiles at once, you would reissue the "SET COMPXEQ" or "SET COMPXEQ subfile-name" command, in effect beginning over again. Alternatively. you can issue the SET NOCOMPXEQ command, which also clears all current COMPXEQ subfiles.
The SHOW COMPXEQ command will tell you all the COMPXEQ subfiles that are currently set, starting with the primary. The IN ACTIVE option can be used to place the output of the SHOW COMPXEQ command into the active file. [See A.2.9.1.] The value of the primary COMPXEQ subfile is maintained as $COMPXEQ. However, the $COMPXEQ variable cannot be used to determine whether more than one COMPXEQ subfile has been set.
By default, the "PUBLIC PROTOCOLS" subfile is set as the COMPXEQ subfile when you enter SPIRES. You can use the SET COMPXEQ command to reset some other subfile as the COMPXEQ file.
Virtually any protocol will execute run more efficiently if it is compiled. In fact, if a protocol contains many compilable statements, it may be faster than an uncompiled protocol by more than an order of magnitude.
The following types of statements are compiled:
LET EVAL IF JUMP GOTO SHOW EVAL XEQ PROC ++label-name
These commands compile even when they are preceded by the slash prefix (/).
Comment statements, preceded by a "-", are left out of compiled code. A NOECHO marker, "!", is stripped from any statements it precedes, since compiled protocols never echo in any case.
Several techniques can be observed to allow more code to be compiled, and to provide quicker execution time of compiled code.
- 1) Declare variables with the proper TYPE in a local VGROUP.
LET M = #N + 2 LET X = $ASK '.' $ACCOUNT
M and N should be TYPE = INTEGER and X should be TYPE = STRING.
- 2) Do not perform unnecessary conversions.
LET X = $INT(17)
If X is declared TYPE = INTEGER, then the $INTEGER function is unnecessary and time consuming.
- 3) Do not have variables posing as more than one TYPE.
LET X = $WDST + 100 LET X = 'END OF PROCESSING.'
Use separate variables. If this is not possible, then the variable must be give a declaration of TYPE = DYNAMIC, which is the least efficient TYPE.
- 4) Rewrite IF statements so that $ variables and $ functions are on the left of the relational operator.
If there are no $ variables or functions, rewrite IF statements so that # variables are on the left of the relational operators. For example, the first pair of statements below should be rewritten as the second pair.
a) IF 'GA.ABC' = $ACCOUNT THEN RETURN b) IF #COUNTER > $INTEGER($ASK) THEN RETURN a) IF $ACCOUNT = 'GA.ABC' THEN RETURN b) IF $INTEGER($ASK) < #COUNTER THEN RETURN
- 5) Eliminate any unnecessary "/" statements.
/ IF #X > 2 THEN * #X is right, you win! IF #X > 2 THEN /* #X is right, you win!
Although statements preceded by the slash prefix can be compiled, there will still be an efficiency loss in using the prefix where it isn't needed.
Several cautions must be observed when compiling protocols that access variables created or altered by other protocols and formats. These common variables must be declared in a global vgroup that is added to the VGROUPS subifle and compiled. [See 4.1.] The global vgroup must be declared in a DECLARE GLOBAL VGROUP command block in the compiled protocol. [Or the global vgroup may be named in an ALLOCATE statement in the SYS PROTO record for the compiled protocol, if you are using the SYS PROTO method of protocol compilation. [See 10.8.]]
Reminder: There are two types of vgroups: local and global. Global vgroups are defined in Vgroups records and compiled separately. Local vgroups are defined in a DECLARE VGROUP command block, in a SYS PROTO record (old way) or in a format definition, and are compiled when the protocol or format is compiled.
The variables in a local vgroup cannot be accessed outside the format or protocol in which they are defined. [That is, they cannot be accessed by other program components. You can work with them at a BREAK XEQ pause, or, for a format's local vgroup, anytime in command mode while the format is set.] They are allocated (exist in memory) only as long as the protocol is being executed or the format is SET. The ALLOCATE command in SPIRES cannot be used to allocate a local vgroup, and local vgroups allocated by a "SET FORMAT format-name" or "XEQ FROM compiled-protocol-name" command always define a new vgroup in core, even if a vgroup with the same name already exists. Local vgroup variables always disappear when you CLEAR FORMAT or terminate XEQ of a compiled protocol.
A compiled protocol calling another compiled protocol, or a compiled protocol calling a format, cannot pass variables defined in a local vgroup, even if the variables are given the same name in each. However, variables declared and compiled in global vgroups can be shared and accessed by formats and protocols.
In short, if the variables from one protocol are needed by other protocols, then
- 1) the variables must be defined in a global vgroup.
- 2) each protocol must contain a DECLARE GLOBAL VGROUP command block with an ALLOCATE statement for the vgroup (or name the vgroup in an ALLOCATE statement in the SYS PROTO record, if the old method of compilation is used).
- 3) If the protocols will execute in succession, then the initial protocol using the global variables must issue an "SET VGROUP vgroup-name" command, and the last protocol using the variables should issue a "CLEAR VGROUP vgroup-name" command. This keeps the vgroup in memory after the first protocol completes execution. (These two commands are not necessary if all the protocols are executed from within a single protocol, and all the protocols declare the same global vgroup.) [See 4.3.]
If a protocol and format must use and communicate the same variables, then the above rules should also be followed.
By using both the SET XEQ and SET COMPXEQ commands simultaneously, it is possible for compiled and non-compiled protocols to call each other. While this facility is invaluable for debugging, it could lead to confusion if a particular protocol exists in both the XEQ and COMPXEQ subfile. For this reason, it is suggested that the SET NOXEQ command be issued when only compiled protocols are to be executed, and SET NOCOMPXEQ be issued when only non-compiled protocols are to be executed.
For any XEQ FROM or .. command, whether issued interactively or within a protocol, SPIRES checks to see whether a record by that name has been preloaded (with the LOAD PROTOCOL command), and, if so, executes the loaded copy of the record. If no record with that name has been preloaded, SPIRES acts as follows:
- 1) If both XEQ and COMPXEQ have been SET, SPIRES first looks for the protocol in the XEQ subfile. If the protocol's found there, it's executed in its non-compiled form. If no such record is found in the XEQ file, SPIRES looks for it in the COMPXEQ subfile, and executes its compiled form. (If more than one COMPXEQ subfile is SET, then the protocol is sought in the secondary subfiles, in the order in which they were SET.) [See 10.1.]
- 2) If only COMPXEQ is SET, then the compiled protocol is looked for and executed from the COMPXEQ subfile.
- 3) If only XEQ is SET, then the non-compiled protocol is looked for and executed from the XEQ subfile.
If neither XEQ nor COMPXEQ are SET, or the protocol cannot be found in the subfile(s) examined, the XEQ FROM or .. command gives an error message to the user.
For large protocols it can be useful to gather statistics on the amount of space that the different components of your protocol are taking up in their compiled form (i.e., when stored by SPIRES as internal tables). These statistics can serve as an advance warning when some component of your protocol (or perhaps the protocol as a whole) is close to exceeding the maximum size allowed.
To obtain statistics, append the STATISTICS option to your COMPILE or RECOMPILE command, as in the example below:
-> select sys proto -> recompile gq.pub.buildjcl statistics Protocol Statistics for FASTADD.JCL 268 of 4088 Bytes ( 6%) ++Label Table 444 of 4088 Bytes (10%) ++Label Names 112 of 4088 Bytes ( 2%) Long Strings 1520 of 8184 Bytes (18%) Functions/Expressions 9512 of 32768 Bytes (29%) Commands 22 of 16384 Bytes ( 0%) Allocate Tables 2 of 16834 Bytes ( 0%) Local Vgroups 11880 of 65536 Bytes (18%) Overall Limit -Compiled protocol: FASTADD.JCL -Protocol compile finished
The STATISTIC option can be abbreviated to STAT.
To direct the statistics to your active file instead of to the terminal, use the IN ACTIVE prefix:
-> in active compile gq.pub.buildjcl stat -Compiled protocol: BUILDJCL -Protocol compile finished
An asterisk at the beginning of a line indicates a table that exceeds 90% of the maximum size allowed. For instance, if the table for commands were to reach 98% of its allowed size, the statistics for the "command" table would look as follows:
*32310 of 32768 Bytes (98%) Commands
(SPIRES would issue this information in a warning message if you omitted the STATISTICS option.) Note that the "Overall Limit" is less than the sum of the tables above it -- you might exceed the overall limit even if the size of the other tables is within the allowed limit.
What happens when a table exceeds the maximum allowed? In the statistics display, the line is marked by an asterisk and the percentage is designated as (**%). In addition, depending on how hard and fast the table limit is, the compiled code may not execute as you intend it to. If your protocol is in danger of hitting a table limit, consider breaking it into multiple protocols to be executed by "chaining". [See 2.2, 2.2.1.]
For information on obtaining statistics while compiling a file definition or a format, see the manuals "SPIRES Formats" and "SPIRES File Definition". See 4.1.2 in this manual for information about statistics for compiled vgroups.
The method described in the first few sections of Chapter 10 [See 10.1, 10.2.] is the preferred way of compiling a protocol. Another older way, which is more trouble to do but which may be easier to continue using if you are already using it, is described in this section. It is still included in this chapter only for compatibility with older applications; it offers no benefits over the newer method, so you should not use this method if you are starting anew.
Whichever method you use, SPIRES will create the same compiled code, which it places into its COMP PROTO subfile. Hence, when you execute a compiled protocol, it makes no difference which method was used to create it; you don't set anything differently to use protocols compiled with one method vs. the other.
Here are the steps for compiling a protocol the old way:
- Step 1: Create a record in your active file to define your protocol's characteristics and add the record to the SYS PROTO subfile.
- Step 2: Issue the COMPILE command followed by the key of the SYS PROTO record created in Step 1.
- Step 3: Issue the SET COMPXEQ command (instead of SET XEQ), followed by the name of the subfile containing the protocol, in order to set the compiled protocol for execution.
The example below shows the user GQ.JPR compiling a protocol named CHECKACCT that is stored in a protocol subfile named TEST PROTOS. (The filename of this protocol subfile is GQ.JPR.TESTPROTOS.)
The record would look something like this:
ID = gq.jpr.sysrecord; FILE = gq.jpr.testprotos; (See below for a full RECORD = proto; explanation of each of PROTOCOL = checkacct; these elements) ALLOCATE = orv.gq.jpr.checkvars;
Then add the record to the SYS PROTO subfile as follows:
-> select sys proto -> add
Use the COMPILE command to compile the SYS PROTO record:
-> compile gq.jpr.sysrecord <--(Same value as for ID above) -Compiled protocol: CHECKACCT -Protocol compile finished ->
To use the compiled protocol, you need to tell SPIRES what subfile contains it (with the SET COMPXEQ command), and then you start the protocol's execution with the "XEQ FROM protocol" or "..protocol" command.
The first two steps are described in a little more detail below.
To compile a protocol, you must first add a record "defining" that protocol and its variables to a public subfile called SYS PROTO. The basic structure of a record in the SYS PROTO subfile is as follows:
ID = gg.uuu.anyname; FILE = gg.uuu.source-file-name; (*) RECORD = record-name-in-source-file; VGROUP = local-variable-group-name; VARIABLE = variable-name; TYPE = variable-type; VARIABLE = variable-name; TYPE = variable-type; OCC = subscripted-variable-occurrence-count; LEN = length-of-non-defaulted-variable; PROTOCOL = protocol-name; ALLOCATE = local-variable-group-name; ALLOCATE = global-variable-group-name;
Note: (*) the FILE element names the protocol subfile where the protocol is stored -- generally you will be the file owner of that subfile, except for Prism protocols, which are stored in a system subfile.
A record such as this must be added to SYS PROTO for each protocol or group of protocols (see note 3 below) that is to be compiled.
When defining the SYS PROTO record, note the following:
- 1) The VGROUP structure is optional; it may occur multiply, but it usually has one or no occurrences (see 3, below). All user variables used in the protocol should be defined either here, or in a global vgroup definition. [See 4.1.1.]
- 2) The VARIABLE structure is multiply occurring. Variable TYPE must be declared; OCC need only be declared for subscripted (array) variables, unless these variables are declared TYPE = DYNAMIC. A LEN may also be declared, if the TYPE of variable has no default length.
- 3) The PROTOCOL structure is multiply occurring, though in most cases it will occur once, naming a single protocol to be compiled with the local Vgroup defined in the SYS PROTO record. However, the SYS PROTO record can name multiple protocols in the protocols file to compile together, each of which may use a different combination of the local vgroups defined in the SYS PROTO record.
- 4) The ALLOCATE element is multiply occurring. It specifies the names of local and global Vgroups that must be examined by the compiler in order to resolve variable references as the protocol is compiled. The local vgroup (or vgroups) defined in the VGROUP structure should be named here for each protocol that should allocate it. [See 4.1 for information about Vgroup definition.]
Once you have written the SYS PROTO record, select SYS PROTO in SPIRES and issue the ADD command to add it to the subfile.
To compile the protocol, you must give the name (ID) of the SYS PROTO record that defines the protocol in a COMPILE command:
-> compile gg.uuu.anyname
If the protocol has been compiled before, use the RECOMPILE command instead of the COMPILE command.
To obtain statistics on the compiled protocol, you can append the STATISTICS option to the COMPILE or RECOMPILE command. [See 10.7.]
If all goes well with the compilation, you will receive the following informational messages:
-Compiled protocol: protocol-name -Protocol compile finished
At that point, you can issue the SET COMPXEQ command and go on to execute the compiled protocol.
If you get compilation errors, you will need to make changes to either the protocol or the SYS PROTO record or both. If you need more information about the messages, use the EXPLAIN command. [See 10.2 for a discussion of some common compilation errors.]
The older method of compiling protocols, described in the previous section, offers no advantages to the newer method. However, the newer method does provide some benefits, such as better checking of variable definition and use, and simpler metadata management (that is, no SYS PROTO records to deal with).
If you do decide to switch to the new method from the old, be aware that you do not have to convert all your old compiled protocols that work fine; they will continue to do so. Whichever method you use, SPIRES will create the same compiled code and store it internally in the same place.
But when the new method was introduced at Stanford, almost all the large applications (each with dozens of protocols) took some time (basically no more than a day's worth of effort) to convert their protocols to the new way. This was worthwhile because the programmers in each application area wanted a single method for compiling their protocols, and the new method's superiority made it the way they wanted to work in the future.
If you do decide to convert the protocols of an application from the old method to the new, you should follow these steps, described in detail below:
- i) Copy the VGROUP structure (for local vgroups) or the ALLOCATE statements (for global vgroups) from the SYS PROTO record for the protocol into the protocol source record using the DECLARE VGROUP and DECLARE GLOBAL VGROUP constructs; then put the protocol back into your protocols subfile.
- ii) Use the ZAP SYS PROTO command to zap the compiled and source SYS PROTO records for the old version.
- iii) Select your protocols subfile and COMPILE the protocol.
- i) Be sure to use the DECLARE VGROUP and DECLARE GLOBAL VGROUP constructs to define/declare any variables and vgroups needed by the protocol.
- ii) Select your protocols subfile and COMPILE the protocol.
If you used PERFORM BUILD PROTOCOLS to create your protocols file, all you need to do is recompile its file definition in order to use the new method. Changes to the $PROTOCOL record definition were made in order for the new compilation method to work, so your file definition must be recompiled to use the new $PROTOCOL characteristics. (If you created your protocols file with PERFORM BUILD PROTOCOLS after the new method of compilation was announced, then you can skip this step. If you created your protocols file in some other way, you should consider using PERFORM BUILD PROTOCOLS to create a new protocols file; otherwise, you won't be able to take advantage of the new method.)
First, find the name of the file holding your protocols subfile, if you don't already know it. To do this, you can select the protocols subfile and issue the command SHOW SUBFILE INFORMATION.
Then, select the FILEDEF subfile, and issue the RECOMPILE command, naming the file:
-> select filedef -> recompile gq.jnk.protos -File definition compiled ->
Your protocols file is now ready for the new compilation method.
If you don't do this step, then when you try to compile a protocol with the new method, you'll get an error like this:
-> select jnk protocols -> compile test.add.address -Compile not possible - no Usemproc values ->
If you see this message, return to this step and verify all the points mentioned here.
Under the new method of compilation, all user variables in a protocol should be declared in either a local vgroup defined at the start of the protocol:
* Test.Add.Address Declare Vgroup Local Variable = Input; Occ = 3; Type = String; Variable = Outside.Input; Type = Dynamic; EndDeclare Declare Global Vgroups Allocate = orv.gq.jnk.test.address.vars; EndDeclare ...
The DECLARE VGROUP and DECLARE GLOBAL VGROUPS constructs are discussed elsewhere in this manual. [See 4.1, 4.1.0.]
Although you can create a dynamic element anytime you like in a compiled protocol, you will receive an error message when you compile; in general, all variables should be declared as part of a local or global vgroup, for better variable management. You can, of course, pre-define a dynamic element as shown in the example above. [See 4.4.]
In protocols compiled with the old method, the local and global vgroup declarations appear in the SYS PROTO record. To use the new method, you need to move that information from the SYS PROTO record into the protocol.
A system utility called PERFORM COMPPROTO can help you make these changes. Not only will it copy the vgroup data from the SYS PROTO record into the source record, but it will also zap the SYS PROTO record in its compiled and source forms from the appropriate system files (see step 2ii). Note that it adds a copy of the SYS PROTO record at the end of the protocol in the form of comments; eventually you will want to get rid of that, but at first, it may make you feel more comfortable to have a copy of the record around.
-> sel jnk protocols -> perform compproto Protocol compilation converter, version 93.12 This utility converts a protocol compiled via SYS PROTO to one that can be directly compiled from the source. It copies variable information into DECLARE VGROUP statements at the top of your protocol and ZAPs the SYS PROTO record. -OK to clear? ok Protocol name? test.add.address SYS PROTO record key? (return=GQ.JNK.TEST.ADD.ADDRESS) -Copying information from SYS PROTO record GQ.JNK.TEST.ADD.ADDRESS -Zapping SYS PROTO record GQ.JNK.TEST.ADD.ADDRESS Your converted protocol is in your active file. The old SYS PROTO has been saved as comments at the end of the protocol. Next: - Review the code for accuracy - Issue the UPDATE command - Issue the COMPILE TEST command Good luck! -> list 1. * TEST.ADD.ADDRESS 2. declare global vgroups 3. allocate ORV.GQ.JNK.TEST.ADDRESS.VARS; 4. enddeclare 5. declare vgroup LOCAL 6. var Input; Type string; Occ = 3; 7. var Outside.Input; Type dynamic; 8. enddeclare 9. ... [the protocol commands] 416. 417. - Protocol converted from SYS PROTO compile to direct compile 418. - by PERFORM COMPPROTO on 02/26/94 at 13:58:52. 419. - Old SYS PROTO record: 420. 421. - ID = GQ.JNK.TEST.ADD.ADDRESS; 422. - MODDATE = SAT. FEB. 26, 1994; 423. - DEFDATE = WED. JAN. 18, 1984; 424. - FILE = GQ.JNK.PROTOCOLS; 425. - RECORD-NAME = PROTO; 426. - VGROUP = LOCAL; 427. - VARIABLE = INPUT; 428. - TYPE = STRING; OCC = 3; 429. - VARIABLE = OUTSIDE.INPUT; 430. - TYPE = DYNAMIC; 431. - PROTOCOL = TEST; 432. - ALLOCATE = ORV.GQ.JNK.TEST.ADDRESS.VARS; 433. - ALLOCATE = LOCAL; -> addupdate ->
You should get rid of both the compiled and source SYS PROTO records created for the old method before you compile with the new method. The PERFORM COMPPROTO command will take care of that for you.
If you do it yourself, the best way is to simply issue the ZAP SYS PROTO command with the SOURCE option:
ZAP SYS PROTO sysproto-record-id SOURCE
Next, select the protocols subfile where you store the protocol, and issue the COMPILE command:
-> select jnk protocols -> compile test.add.address -Compiled protocol: TEST.ADD.ADDRESS -Protocol compile finished ->
If you have any problems, you may need to make changes to the protocol, put it back in the subfile (UPDATE) and try compiling again.
The new method is described in detail earlier in this chapter. [See 10.2.] Be sure to use the DECLARE VGROUP and DECLARE GLOBAL VGROUP constructs when your protocol uses variables. [See 4.1, 4.1.0.]
NOTE: In the following examples, the line numbers in parentheses are correlated with line numbers in the comments after each example. The numbers are not part of a protocol command stream.
(1) * PERFORM (2) ! SET MES = 0 (3) !SET NOSTOP (4) !LET GOOD = $FALSE (5) !/$ASK (6) !IF $YES: LET GOOD = $TRUE (7) !SET STOP (8) ! SET MES = 2 (9) !RETURN (10) !- (11) !- AUTHOR: L.E. ROSEN (12) !- This protocol will execute a SPIRES command passed in $ASK, and (13) !- will set GOOD to true or false, depending upon the success of (14) !- the command.
Line Comments 2-3 Suppress all system messages and suppress BREAK XEQ on command failure 5 Execute the command passed in the parameters list to $ASK 6 If successful, alter value of GOOD assigned in 4 7-8 Reset system messages and automatic BREAK XEQ to sign-on default 9 Return to calling protocol (or command level)
(1) * PAGINATE (2) * (3) * If you have any carriage control characters in your data set, (4) * they will be moved to column 2. You may modify them at 'X-?'. (5) * In response to the "X-?" prompt: set length to the length of (6) * your longest line (approximately) and get data set into active file. (7) * (8) ! BREAK XEQ (9) ! LET '$$ECHO' = '' (10) ! IF $NOECHO : LET '$$ECHO' = NO (11) !SET NOECHO (12) - AUTHOR: J R Sack, Polya 162 (13) - DESCRIPTION: inserts carriage control for new page at a (14) - line user chooses from a range the protocol lists. Also (15) - inserts an incremented page number at the foot of the page. (16) ASK PRO='Maximum Lines per page (must be < 60)? ' (17) /LET LINEEND = $INT($ASK) (18) /LET LINESTART = $ASK - 6 (19) CHANGE 1 TO ' ' IN ALL NOLIST (20) SET WDSR (1) (21) WDSR (22) LET FIRSTLINE = $WDSR (23) LET PAGENO = 0 (24) ++NEXT.PAGE (25) /SET WDST (#LINESTART) #FIRSTLINE / LAST (26) LET START = $WDST (27) /SET WDST (#LINEEND) #FIRSTLINE / LAST (28) LET PAGENO = #PAGENO + 1 (29) IF $WDST < #LINEEND : JUMP EXIT (30) /LIST #START / $WDST (31) ASK PRO='First line of new page? ' (32) LET FIRSTLINE = $LINE($ASK) (33) /LET WDSW = #FIRSTLINE -.002 (34) /SET WDSW = #WDSW (35) /WDSW #PAGENO (36) /CENTER $WDSW LENGTH=$LENGTH (37) /CHANGE 1/1 TO '7' IN $WDSW NOLIST (38) /LET WDSW = #FIRSTLINE - .001 (39) /SET WDSW = #WDSW (40) WDSW 1 (41) JUMP NEXT.PAGE (42) ++EXIT (43) /WDS #PAGENO (44) /CENTER $WDSW LENGTH=$LENGTH (45) /CHANGE 1/1 TO '7' IN $WDSW NOLIST (46) * (47) /* Your active file has #PAGENO pages. (48) * Be sure to use 'CC' in the list offline options. (49) * (50) /SET #'$$ECHO'ECHO
Line Comments 2-7 Type messages to terminal 8 Return control to user to access the text to be paginated 9-11 Save entry value of $ECHO, then reset $ECHO 17-18 Set a linerange between which the page may be broken 20-23 Initialize several variables 25-26 Find the first line of the range in which a page break can occur 27 Find the last line of the range in which a page break can occur 28 Increment the page number counter 29 If less than a page is left, jump to a concluding procedure 32 Convert the string response to a line variable 33-37 Insert a skip to bottom line with page number centered in it 38-40 Insert a line with a skip to new page carriage control 43-49 Put in last page number and exit protocol 50 Reset user's $ECHO setting
(1) * CLEAR.TEXT (2) ! SET WDST = LAST (3) ! IF $WDST < 0 : JUMP EXIT (4) ! IF $CLEAR : JUMP CLEARTEXT (5) ! ASK UPPER PRO='CLEAR TEXT?' ATTN='JUMP XEQ.ABORT' (6) ! LET M = $MATCH($ASK,YES,YE,Y,OK,O) (7) ! IF #M >= 1 : JUMP CLEARTEXT (8) ! ++XEQ.ABORT (9) * (10) * XEQ ABORTED. DO NOT DO THIS PROTOCOL UNTIL THE ACTIVE (11) * FILE CAN BE CLEARED. YOU WILL BE RETURNED TO SPIRES. (12) * (13) ! ..RESTORE.SETTINGS (14) ! CLEAR XEQS (15) ! ++CLEARTEXT (16) ! CLEAR TEXT (17) ! ++EXIT (18) ! SET CLEAR (19) ! RETURN (20) - Author: J R Sack, Polya 162 (21) - Description: asks if a clear text can be executed; if yes, (22) - then the protocol clears text; if no, the user is returned (23) - to SPIRES command mode after an error message. (24) - "..save.settings" must be xeq before a "..clear.text"
Line Comments 2-3 If no active file, don't ask to clear it 4 If user had set $CLEAR, automatically clear text 5-7 Jump if a response matches 8-14 Abort XEQ and return to command mode if match fails 15-19 Clear text, set auto clear, return one level 24 If XEQ is aborted, then RESTORE.SETTINGS will be executed, this protocol uses variables defined by SAVE.SETTINGS; the calling protocol must issue a RESTORE.SETTINGS of its own to reset $CLEAR to its sign on value
(1) * LIST.PROTOCOL (2) ! ..SAVE.SETTINGS (3) ! SET NOECHO NOLIST (4) ! ..CLEAR.TEXT (5) /SELECT $XEQ (6) ++ASK (7) ASK UPPER PRO='WHICH PROTOCOL?' ATTN='JUMP XEQ.ABORT' (8) /..PERFORM TRANSFER $ASK (9) IF #GOOD: JUMP TRANSFER.OK (10) * (11) /* '$KEY' is not a protocol in '$XEQ' (12) * (13) JUMP ASK (14) ++TRANSFER.OK (15) /LIST OFFLINE UPLOW '*** SPIRES PROTOCOL ($DATE at $TIME) ***' (16) CLEAR TEXT (17) ++XEQ.ABORT (18) !..RESTORE.SETTINGS
Line Comments 2 XEQ a protocol that saves all of a user's settings 3 Alter settings 4 XEQ a protocol that prompts if clear text is OK 5 SELECT The subfile for which XEQ is SET 7 Prompt for protocol name 8 Attempt to transfer the protocol name given using PERFORM 9-13 If transfer fails print an error message and reprompt 14-15 List the transferred protocol offline 16 Clean up the active file 17-18 Restore the user's settings to their value before XEQ
(1) * SHOW.FILE.PERMITS (2) ! ..SAVE.SETTINGS (3) ! SET WARN MES = 0 (4) ! SET WDST = 'ORVYL FILE SYSTEM' 1 (5) ! IF $WDST > 0 : ..CLEAR.TEXT (6) ! DUMP FILES (7) ! SET WDSR = 'ORVYL FILE SYSTEM' 1 (8) ! / SET WDST = $WDSR (9) ! WDSR (10) ! ++LOOP (11) ! WDSR END='JUMP EXIT' (12) ! / SHOW PERMITS $ASK (13) ! JUMP LOOP (14) ! ++EXIT (15) ! / DELETE $WDST/LAST (16) ! ..RESTORE.SETTINGS (17) ! RETURN (18) - THIS PROTOCOL SHOWS FILE PERMITS ON ALL THE USERS (19) - ORVYL FILES (20) - AUTHOR: JOHN R. SACK, USER SERVICES, POLYA 162
Line Comments 2 Save user's settings 3 Cancel warning messages caused by protocol XEQ 4 See if this string is in the active file in column one 5 If yes, then execute the CLEAR.TEXT protocol 6 If no, add ORVYL file names to end of the active file using the ORVYL 'DUMP' command 7 Save the line at which the dump began in $NEXTWDSR and then save it in $WDST 9 Read the line whose number is stored in $NEXTWDSR; this line number was set by the SET WDSR in 7 11 Read each line into $ASK following the first line dumped 12 Execute a SHOW FILE PERMITS command for each file name stored in $ASK by the preceding WDSR command
(1) * SAVE.SETTINGS (2) !LET '$INFOX' = $INFOX (3) !LET '$WARNX' = $WARNX (4) !LET '$SERX' = $SERX (5) !LET '$ECHO' = '' (6) !LET '$LIST' = '' (7) !LET '$STOP' = '' (8) !LET '$DIAG' = '' (9) !LET '$CLR' = '' (10) !LET '$CASE' = UPLOW (11) !LET '$LEN' = $STR($LENGTH) (12) !IF $NOECHO THEN LET '$ECHO' = NO (13) !IF $NOLIST THEN LET '$LIST' = NO (14) !IF $NOSTOP THEN LET '$STOP' = NO (15) !IF $NODIAG THEN LET '$DIAG' = NO (16) !IF $NOCLR THEN LET '$CLR' = NO (17) !IF $UPPER THEN LET '$CASE' = UPPER (18) !LET '$SELECT' = $SELECT (19) !LET '$FORMAT' = $FORMAT (20) !RETURN (21) - Dick Guertin, SCIP
[See 11.7 for comments.]
(22) * RESTORE.SETTINGS (23) !/SET LENGTH #'$LEN' (24) !/SET #'$CASE' (25) !/SET #'$ECHO'ECHO #'$LIST'LIST #'$STOP'STOP #'$DIAG'DIAG #'$CLR'CLR (26) !/SET INFO MES #'$INFOX' (27) !/SET WARN MES #'$WARNX' (28) !/SET SER MES #'$SERX' (29) !IF $SELECT = #'$SELECT' THEN GOTO FORM (30) !IF $SELECT > ' ' THEN CLEAR SELECT (31) !++FORM (32) !IF #'$SELECT' <= ' ' THEN RETURN (33) !/SELECT #'$SELECT' (34) !IF #'$FORMAT' > ' ' THEN /SET FOR #'$FORMAT' (35) !RETURN (36) -- Dick Guertin, SCIP
Line Comments 2-4 System defined variables are saved in dynamic variables, which must be enclosed in apostrophes because they begin with a special character, '$' 5-11 Dynamic variables that may be left over from a previous SAVE or RESTORE.SETTINGS are initialized 12-17 String prefixes are assigned for certain flag variables; these prefixes are reset in RESTORE.SETTINGS line 25 23-34 System defined variables are reset; note that the variables' names all begin with '$' to insure that they are different from any variables a user may himself have dynamically defined. The value of a variable whose name begins with '$' must appear enclosed in apostrophes and prefixed by a '#' (the '#' is required for all uses of a variable's value). #'variable.name' is the form when variable.name contains special characters such as '$'.
(1) * PAGE.TO.SECTION (2) ! SET NOECHO (3) ++LOOP (4) LET TOP = $NEXTWDSR (5) / SET WDSR 'page-XX' IN #TOP/LAST (6) IF ~$NEXTWDSR : RETURN (7) / LIST $NEXTWDSR (8) ASK PRO='Sec ' (9) LET SECTION = $ASK (10) / CHANGE 'page-XX' to 'Sec #SECTION ' in $NEXTWDSR (11) JUMP LOOP
Line Comments 4 Begin the line range with the last line found, in case the string being search ed occurs more than once per line 5 Search for the first line with the string 'page-XX' in the indicated range 6 If a line is not found, $NEXTWDSR is 0 or FALSE, then return to next higher level, usually command mode 7 List the line found; the line number found by a SET WDSR command is stored in $NEXTWDSR until a WDSR command is done 8-9 Prompt for the string that 'page-XX' is to be changed to in line 10 10 Execute a change command for the line 11 Jump to search for another line
The following set of exercises will provide a helping hand through some of the elementary steps in building and maintaining protocols under SPIRES, especially for those who are unable to attend the User Services short courses on SPIRES but do have access to a terminal.
The exercises will give you experience with the commands and concepts discussed in Sections 2 and 3 of the SPIRES/370 Protocols manual. In addition you will become familiar with the procedures necessary to:
- Examine protocols in the PUBLIC PROTOCOLS subfile
- Build a private protocol subfile
- Add and maintain protocols in a private protocol subfile
- Establish a SPIRES entry protocol
Note: Section numbers in parentheses refer to the SPIRES/370 Protocols manual.
These exercises were written by Carol Lennox.
PART A: Logon to the system and call SPIRES. Type in the following command: -> SHOW LINE OPERATOR Now type in the following two commands: -> LET X = OPERATOR -> / SHOW LINE #X
Q1: Did you get the same response from the system for both sets of commands?
Q2: What would happen if you did not include the '/' substitution prefix at the front of the second line? If you are not sure try the command : -> SHOW LINE #X
PART B: Now try the following command: -> / SHOW LINE $TERMINAL
Q1: What line did the system give you a response for?
Q2: Would you get the same information if you did not include the '/' substitution prefix?
PART A: Instead of entering each command directly from the terminal, as in exercise 1, let's now collect the same four commands in the active file and execute them as a SPIRES protocol. Logon and call SPIRES then type in the following commands and data: -> CLEAR ACTIVE -> COLLECT 1. > SET ECHO 2. > SHOW LINE OPERATOR 3. > LET X = OPERATOR 4. > / SHOW LINE #X 5. > / SHOW LINE $TERMINAL 6. > (attn) -> XEQ You have just executed the commands in the active file as a SPIRES protocol.
Q1: Did you get the same responses from the system as in exercise 1?
Q2: Are the commands you stored in the active file still there after executing them as a protocol? If you aren't sure, use the LIST command to see if anything is in the active file.
PART B: You notice that each command in the protocol was listed by SPIRES at your terminal before it was executed. This is called ECHO mode and is controlled by the SET ECHO and SET NOECHO commands. If you would like to inhibit the listing of the commands, change line 1 in the protocol as follows and re-execute the protocol: -> CHANGE 'ECHO' TO 'NOECHO' IN 1 -> XEQ This is called NOECHO mode and causes all printing of the protocol commands to be suppressed when the protocol is executed. Notice that the command SET NOECHO is itself echoed. To prevent this, simply put an "!" in front of the SET NOECHO command. PART C: Now let's save the protocol collected in the active file as a member of your WYLBUR library data set and then see how we could retrieve the protocol at a later time for execution. First we will have to save the protocol and then clear the active file by typing the following: -> SAVE #TEST1 -> CLEAR ACTIVE The active file is now empty. In order to retrieve the protocol from your library and execute it, you need only type in the following two commands: -> USE #TEST1 -> XEQ
Q1: Were the system responses the same as for part A ?
Q2: If you logon to the system tomorrow at a different terminal, call SPIRES and then "USE" and "XEQ" the protocol as you did above, would the system still give you the same response? If so, why? If not, why not?
PART A: First let's simply try printing the current value of several system variables. Logon to the system and call SPIRES. Now type in the following commands (the system responses are not shown): -? / * $TERMINAL -? / * $DATE -? / * $TIME -? / * $LENGTH -? / * $NAME
Q1: Can you verify any of the values printed in the system responses: e.g., using WYLBUR commands like SHOW DATE and SHOW LINE?
Q2: Would the system responses change in any way if you did not type a "/" substitution prefix at the beginning of each command? If you are not sure, try it.
PART B: Now let's establish a value for a user-defined variable and then see if we can cause the values to print at the terminal. Type in the following commands: -? LET X=10 -? * #X -? / * #X
Q1: What is the difference in the system response between lines 2 and 3?
Q2: Which symbol in both lines 2 and 3 indicated you wanted to refer to the value associated with the variable named X?
Q3: Why wasn't the value of X printed for the second command? Did the use of the # symbol automatically cause the value of X to be substituted in the system response?
Q4: Which symbol in the third line actually forced the system to substitute the value 10 for the symbol #X in the response? PART C: Now let's build a protocol in the active file that will print both system and user-defined variable values at the terminal. Type in the following commands: -? SET NOECHO -? COLLECT CLEAR 1. ? * 2. ? /* HELLO THERE, $NAME... 3. ? /* I SEE YOU ARE USING TERMINAL $TERMINAL TODAY 4. ? /* FOR YOUR RECORDS IT IS NOW $TIME AND THE DATE IS $DATE 5. ? * 6. ? * LET'S SEE HOW GOOD YOU ARE AT ARITHMETIC... 7. ? * FIRST LETS ADD THE NUMBERS 10 AND 50 8. ? LET SUM=10+50 9. ? /* I GET #SUM ...HOPE YOU DID TOO! 10. ? /* NOW LETS MULTIPLY #SUM BY 5 11. ? LET PRODUCT = #SUM*5 12. ? /* IF YOU DID IT PROPERLY; YOU GOT #PRODUCT 13. ? /* THAT'S ALL FOR NOW, $NAME ...BYE 14. ? * 15. ? (attn) -? XEQ
Q1: What did lines 1, 5 and 14 cause to happen at the terminal?
Q2: Can you explain what the slash at the beginning of lines 2, 3, 4, 9, 10, 12 and 13 does?
Q3: Now let's see what happens if we try to print the value of a variable which has not yet been assigned any explicit value. Type in the command: -? / * #NOVAL Does the system print a "default" value for the NOVAL variable?
Q4: Does the system even seem to consider NOVAL a variable?
PART A: In the protocol in exercise 3 Part C at line 8, we used the LET command to compute or "evaluate" the expression 10+50. In line 11 we again used the LET command to "evaluate" the expression "#SUM*5." In this LET command, the system automatically substituted the value associated with the variable SUM before evaluating the expression. So, we see three important things about the LET command: . Substitution of values for variable names preceded with a # or $ is done automatically, without the use of the "/" substituion prefix. . After substitutions have been performed, the expression to the right of the = sign in the LET command is evaluated. . The result of the evaluation is "assigned" to the variable named on the left of the = sign. Our question now is: if we include an expression in the * command, such as 10+50, will the system "evaluate" that expression and substitute the value 60 in the system response? Let's try the command: -? * 10+50
Q1: Does the system automatically evaluate expressions in * commands? Our next question is: Will the use of the "/" substitution prefix cause evaluation of an expression in the * command? Let's try the command: -? / * 10+50
Q2: Did the substitution prefix cause the expression to be evaluated? PART B: Now let's see if the use of user-defined variables instead of actual numbers results in evaluation of expressions in the * command: -? LET X=10 -? * #X+50 -? / * #X+50
Q1: Did the system evaluate either of the expressions in the * commands? Has the system evaluated any of the expressions in the * commands we have tried so far?
Q2: In fact, SPIRES never evaluates expressions contained in * commands. Expressions will only be evaluated when they occur in the LET, the IF and the EVAL commands.
Q3: Can you now describe the difference between substitution and evaluation?
PART C: Now let's use the LET command to cause an expression to be evaluated and the result assigned to a variable. We can then print the resulting value using the * command. Type in the following commands: -? LET SUM=10+50 -? /* THE SUM OF 10 and 50 is #SUM
Q1: If you want an expression evaluated, and the result printed at the terminal, what are the two commands we have studied so far that you have to use?
Q2: Now look back at the protocol we entered in exercise 3 Part C. Examine lines 8 and 9. Can you now explain why we entered these two lines instead of a line like " /* I GET 10+50 ... HOPE YOU DID TOO!" ?
PART A: First let's collect a very simple protocol in the active file which, when executed, will prompt you for your age and then print it back at the terminal. Logon to the system, call SPIRES, and then type in the following commands: -? SET NOECHO -? COLLECT CLEAR 1. ? ASK PROMPT='HOW OLD ARE YOU? ' 2. ? /* HMMM .... $ASK IS PRETTY DARN OLD! 3. ? * 4. ? (attn) -? XEQ When you are asked "HOW OLD ARE YOU?", enter your age and then press the carriage return.
Q1: To what system variable was your reply from the terminal assigned? If you are not sure type: -? /* $ASK
PART A: Let's now try a protocol which uses the WDSW command to write several lines to the WYLBUR active file. First we will collect the protocol in the active file and execute it, including a LIST command as part of the protocol. Logon to the system, call SPIRES, and type in the following commands: -? SET NOECHO -? COLLECT CLEAR 1. ? CLEAR ACTIVE 2. ? WDSW HELLO THERE 3. ? WDSW THIS LINE IS IN THE ACTIVE FILE BECAUSE A SPIRES 4. ? WDSW PROTOCOL PUT IT HERE USING A WDSW COMMAND 5. ? / WDSW WELL, $NAME, THATS ALL 6. ? LIST UNN 7. ? * 8. ? * 9. ? * IF YOU DONT BELIEVE ME, TRY THE LIST COMMAND YOURSELF! 10. ? * 11. ? (attn) -? XEQ
Q1: Which command in the protocol erased the protocol from the active file?
Q2: Which commands actually caused new information to be written into the active file?
PART A: Section 2.6 discussed the ASK command briefly. It showed an example of an ASK command that contained JUMP commands to handle the situations where the user types only a carriage return or an attention in response to the prompt. Let's now try a very simple protocol which will do different things when executed, depending on your response to the ASK prompt. Logon and call SPIRES then type in the following commands: -? CLEAR ACTIVE -? COLLECT 1. ? !SET NOECHO 2. ? * 3. ? ASK PROMPT='NAME? ' NULL='JUMP SHY' ATTN='JUMP ITCHY' 4. ? /* WELL $ASK; YOU APPEAR TO BE A FRIENDLY SOUL. 5. ? JUMP QUIT 6. ? ++SHY 7. ? * HMM.. LOOKS LIKE YOU ARE RATHER SHY! 8. ? JUMP QUIT 9. ? ++ITCHY 10. ? * REALLY.. YOU DONT HAVE TO HURRY AWAY SO FAST! 11. ? ++QUIT 12. ? * BYE.... 13. ? (attn) -?
PART B: Now let's execute the protocol four times. The first time try typing in your name when prompted; the second time try typing just a carriage return; the third time try hitting the attention key; and the fourth time try typing a space followed by a carriage return.
Q1: Did you get a different response each time?
PART C: Now let's assume we want to simply reprompt the person for his name if he hits only the carriage return. This is the action SPIRES will automatically take if the ASK command has no NULL option specified. Change the protocol in 7 Part A as follows: -? REPLACE 3 3. ? ASK PROMPT='YOUR NAME PLEASE? ' ATTN='JUMP ITCHY' -? DELETE 6/8 Now execute the protocol and see what happens if you respond with only a carriage return. Execute the protocol a second time, responding with a space followed by a carriage return.
PART A: Let's build a protocol which uses the IF command. The protocol will carry on a very simple conversation with whomever executes it, taking different actions depending on the answer typed in. Logon to the system, call SPIRES, and then type in the following commands: -? CLEAR ACTIVE -? COLLECT 1. ? !SET NOECHO 2. ? * 3. ? * I AM THINKING OF A NUMBER BETWEEN 1 AND 10... 4. ? ASK PROMPT='WHAT IS IT? ' NULL='JUMP QUIT' ATTN='JUMP QUIT' 5. ? IF $ASK = 3 THEN * WOW! YOU GOT IT! 6. ? IF $ELSE THEN * TOO BAD...YOU LOSE...IT WAS 3! 7. ? ++QUIT 8. ? * THATS ALL FOR NOW... BYE 9. ? * 10. ? (attn) -? Now execute the protocol several times, changing your response each time, to see the effect of the IF statement.
PART B: Let's now make the protocol a little more sophisticated and include some error checking on the answer. Type in the following commands: -? SET NOECHO -? COLLECT CLEAR 1. ? * 2. ? * I AM THINKING OF A NUMBER BETWEEN 1 AND 10 3. ? ++QUESTION 4. ? ASK PROMPT='WHAT IS IT?' NULL='JUMP NO.GUESS' ATTN='JUMP QUITTER' 5. ? IF $INT($ASK) < 1 THEN JUMP TOO.LOW.HIGH 6. ? IF $INT($ASK) > 10 THEN JUMP TOO.LOW.HIGH 7. ? IF $INT($ASK) = 3 THEN JUMP WINNER 7.1 ? - $INT tells SPIRES to treat the value as an integer 7.2 ? - rather than as a character string. 8. ? /* NOPE, $ASK ISNT IT! 9. ? JUMP QUESTION 10. ? ++NO.GUESS 11. ? * COME ON NOW; TAKE A GUESS! 12. ? JUMP QUESTION 13. ? ++WINNER 14. ? * WOW!!! YOU GOT IT!! CONGRATULATIONS! 15. ? JUMP QUIT 16. ? ++TOO.LOW.HIGH 17. ? * COME ON, THATS NOT BETWEEN 1 AND 10 18. ? JUMP QUESTION 19. ? ++QUITTER 20. ? * SORRY....THE NUMBER WAS 3 21. ? ++QUIT 22. ? * BYE.... 23. ? (attn) -? Execute the protocol several times now, trying numbers like -1, 28, 6 and just carriage return to see the different actions. PART C: Let's further refine the protocol so that it limits the user to only two guesses. We'll do this by establishing a variable which we will use to count the number of tries the person makes which are between 1 and 10 but not the number 3. We will not count a carriage return only or a number outside the range 1 to 10 as a try. Modify the protocol to do this by typing in the following commands. -? INSERT 2.5 2.5 ? LET TRYS=0 -? INSERT 8.2, 8.4, 8.6 8.2 ? LET TRYS = #TRYS+1 8.4 ? IF #TRYS=2 THEN JUMP QUITTER 8.6 ? * ONLY ONE MORE CHANCE -? LIST Study the protocol as it now stands and be sure you understand what the four inserted commands will cause to happen. Now execute the protocol, entering numbers other than 3.
Q1: Can you figure out what changes and additions would have to be made to the protocol so that it would allow up to 5 guesses?
Q2: Can you figure out what changes would be needed to give the person a hint as to the answer, such as 'HINT: THE NUMBER IS LESS THAN 5' once he or she has guessed wrong three times?
PART A: Let's enter a protocol in the active file and then try executing "pieces" of it by specifying the AT and the USING options of the XEQ command discussed in section 3.1. Logon to the system, call SPIRES and type the following: -? COLLECT CLEAR 1. ? !SET NOECHO 2. ? * 3. ? /* HELLO $NAME 4. ? * I AM GOING TO PRINT A TABLE OF SQUARES 5. ? * 6. ? ++ HEADING 7. ? * NUMBER SQUARE 8. ? LET N=1 9. ? ++LOOP 10. ? LET NSQR=#N*#N 11. ? /* #N #NSQR 12. ? LET N=#N+1 13. ? IF #N <= 9 THEN JUMP LOOP 14. ? * 15. ? (attn) -? Now let's try executing only the first seven lines of the protocol, to see if the greeting and table heading are properly spaced. Type in: -? XEQ USING 1/7 Now let's see if the table itself is generated properly. Type in: -? XEQ USING 8/LAST Now let's see if the table heading and the table entries are properly aligned. Type in: -? XEQ AT HEADING
Q1: What would print at the terminal if you typed in the following command? -? XEQ AT HEADING USING 4/7 If you are not sure, try it.
Q2: What difference in printout at the terminal would you expect from the following two commands? -? XEQ USING 8/LAST -? XEQ USING 9/LAST
PART A: Whenever you wish to invoke a protocol which has been stored in a SPIRES protocol subfile, you must do two things: . SELECT the protocol subfile . Use the SET XEQ command to establish it as the default protocol subfile from which future protocols are to be obtained. You can use the SHOW XEQ command at any time to determine whether or not a default protocol subfile is currently established. Logon to the system, call SPIRES, and type in the following: -? SHOW XEQ -? SELECT PUBLIC PROTOCOLS -? SHOW XEQ -? SET XEQ -? SHOW XEQ
Q1: Was there a default protocol subfile established when you typed in the first SHOW XEQ command?
Q2?: Did the selection of the PUBLIC PROTOCOLS subfile automatically cause it to be established as the default protocol subfile? If you are not sure, consider the system response to the second SHOW XEQ command.
Q3: After selecting the PUBLIC PROTOCOL subfile, which command finally established it as the default subfile from which all future protocols will be obtained if an XEQ FROM command is given?
PART B: Now let's examine what happens when we establish a default protocol subfile and then select another subfile we wish to search. Type in: -? SELECT PUBLIC PROTOCOLS -? SET XEQ -? SHOW XEQ -? SELECT RESTAURANT -? SHOW FILE SIZE -? SHOW XEQ
Q1: What was the name of the second subfile we selected, and how many records (restaurant descriptions) did it contain?
Q2: Did the selection and use of the RESTAURANT subfile destroy or change the default protocol subfile we established in the first two commands?
PART A: First let's use SPIRES commands to see what protocols are stored in the PUBLIC PROTOCOLS subfile. Each protocol is stored as a separate record, with the name of the protocol serving as the key of the record. To determine the number of protocols in the subfile and their names, logon to the system, call SPIRES, and type in the following: -? SELECT PUBLIC PROTOCOLS -? SHOW FILE SIZE -? FOR GOAL +? SHOW KEYS ALL +? END
Q1: How many protocols (records) are there in the PUBLIC PROTOCOLS subfile?
Q2: How many "keys" or protocol names were listed by the SHOW KEYS ALL command?
PART B: Using a protocol from the PUBLIC PROTOCOLS file is very simple. Let's try the COMPUTER protocol as an example. This is a protocol evaluates any numeric expression you type in. So let's use it to evaluate a simple expression like 48/3+2. Type in the following to invoke the COMPUTER protocol and when prompted for an expression enter 48/3+2. To terminate the protocol, press the attention key instead of entering an expression. -? SELECT PUBLIC PROTOCOLS -? SET XEQ -? XEQ FROM COMPUTER Now try the ".." form of the XEQ command. Since you have already established PUBLIC PROTOCOLS as the default protocol subfile, you need only type in: -? ..COMPUTER
Q1: Did the execution of the protocol change in any way when invoked by the ".." form of the XEQ command?
Q2: Can you use the ".." form of the XEQ command to execute a protocol stored in the active file? If you are not sure, try it or review section 3.2. PART C: Now let's try executing the COMPUTER protocol with a parameter which indicates that the instructions normally listed are to be bypassed. The parameter follows the ..COMPUTER command. This protocol has been written so that it treats any parameter included in the ".." command as instructions to bypass the explanation. Since the characters in the parameter are unimportant, let's use the word "ANYTHING" as our parameter. Type in the following to see how this works: -? ..COMPUTER ANYTHING
Q1: Did you get any instructions?
Q2: Can you use this parameter option on all forms of the XEQ command or only the ".." form? If you are not sure re-read section 3.2.
PART A: You may be interested in looking at the commands which make up a protocol stored in the PUBLIC PROTOCOLS subfile. One way to do this is to use the normal SPIRES commands for selecting a subfile and displaying a record in that subfile. While most of the protocols in this subfile use features of the protocol language which you may not have studied yet, many of the commands in the COMPUTER protocol, used in the last exercise, should be familiar. Logon to the system, call SPIRES, and type in the following to get a display of the commands in this protocol: -? SELECT PUBLIC PROTOCOLS -? DISPLAY COMPUTER The "!" prefix which you see at the beginning of several of the lines simply inhibits the listing of that command, regardless of the current ECHO setting. You should now be able to understand all other lines in the protocol except the second, third, and fifteenth lines which use some advanced features.
PART B: If you would like to have a display of the protocol with WYLBUR line numbers as well as a copy of the protocol in the active file, type in the following: -? IN ACTIVE CLEAR DISPLAY COMPUTER
Q1: Study the IF command at line 5. What system variable is it testing? Are there any ASK commands ahead of line 5 which could have assigned a user response to $ASK?
Q2: Based on your answers to question 1 and your experience in doing part C of exercise 11, can you guess what value is currently assigned to $ASK at line 5? If you are stumped, look at the JUMP statement in the command.
Q3: Examine the ASK command at line 10. What should happen if you execute the protocol and only type in a carriage return as your response? PART C: Any protocol record in the PUBLIC PROTOCOLS file may be displayed in one of several formats. The format which you saw in part B of this exercise has been named the "PUBLIC" format. If you would like to see the other formats available, type in the following: -? SHOW FORMATS The phrase "(SELECTED)" always appears in the system response to the right of the format name which is currently the default. In addition to these formats, there is a default system format. To see the system default format, type in the following commands: -? CLEAR FORMATS -? SHOW FORMATS -? IN ACTIVE CLEAR DISPLAY COMPUTER Notice that the first line now begins with NAME= and all the rest of the lines now begin with COMMAND=. Furthermore, each line ends in a semicolon. This reflects the structure shown in section 3.2 for every record in any protocol subfile. When a SPIRES file is created, it is up to the file owner to decide whether or not he wishes to ADD, TRANSFER, UPDATE, and DISPLAY records in a form other than the system default. While SPIRES does not require that there be special formats for a subfile, more often than not users wish to add and/or view the records in their subfiles in a format other than the system default. Additionally, file owners often develop formats for generating reports.
Q1: Are the commands displayed between "COMMAND=" and ";" under the default system format the same as those displayed when the PUBLIC format was in effect?
Q2: Can you now see why many people choose to define alternate formats for adding, updating, and displaying records in their subfiles?
PART A: If you plan to develop more than 2 or 3 protocols, you will want to establish your own protocol subfile for storing them. If you have never done a SPIRES file definition, or even if you have, you can use the PERFORM BUILD PROTOCOLS command. This utility not only establishes the file definition needed for your protocol subfile, but also establishes a format definition for your subfile. This format will display your protocol records without the NAME=, COMMAND=, and ";" elements. It will also automatically add the NAME=, COMMAND= and ";" elements to any protocol you have developed in the active file and are adding to your subfile. This will save you a great deal of time and effort in maintaining your protocols. When you execute the PERFORM BUILD PROTOCOLS command, you will be asked for two things: . the name you wish to use for your protocol subfile . a list of accounts, other than your own, that may update or add records to your subfile The utility then builds a file definition for your subfile and adds it to the system FILEDEF file, and compile it for you. To use this utility, simply call SPIRES and issue the command -? perform build protocols For more details about this utility, see Chapter 28 in "Technical Notes" or EXPLAIN PERFORM BUILD PROTOCOLS.
PART A:
Let's create a protocol in the active file and check that it executes properly. Once checked out you will select your protocol subfile and ADD the protocol to it. For the purposes of this example, the name of your protocols subfile is shown as "your-protocol-subfile-name." The protocol to be added will be called PROTONAMES.
The PROTONAMES protocol, when executed, will list out the names of all protocols currently stored in your protocol subfile or a message stating that there are no protocol records in your protocol subfile. The names of protocols which have been removed today will still appear, and those which are added today will not appear.
The first line of the protocol should be a "*" command containing the name of the protocol. While checking out the protocol in the active file, this will cause the protocol name to print at the terminal. However when the protocol is added to your protocol subfile, this first line will be used by the PUBLIC format to create the "NAME=" field of the record. Logon to the system, call SPIRES, and type in the following:
-> COLLECT CLEAR 1. ? * PROTONAMES 2. ? !SET NOECHO 3. ? - lists the names of protocols 4. ? - stored in my protocol subfile or deferred queue. 5. ? SET MES=0 6. ? LET OLDFILE=" " 7. ? IF $SELECTED THEN LET OLDFILE=$SELECT 8. ? SELECT your-protocol-subfile-name 9. ? * 10. ? *PROTOCOL NAME LIST 11. ? *------------------ 12. ? FOR GOAL 13. ? SHOW KEYS ALL END="JUMP NORECS" 14. ? END 15. ? ++ENDING 16. ? IF #OLDFILE~= " " THEN / SELECT #OLDFILE 17. ? SET MES=2 18. ? RETURN 19. ? ++NORECS 20. ? *NO PROTOCOLS CURRENTLY IN SUBFILE 21. ? JUMP ENDING 22. ? (attn) ->
Be sure you put the name of your protocol subfile in line 8. Now execute the protocol from the active file to be sure it works.
Q1: Which of the three messages did you get when you executed the protocol?
Q2: If you selected another file such as FILEDEF and then re-executed the protocol, would the FILEDEF subfile still be selected after the protocol executed? If you are not sure, try typing in the following: -> SELECT FILEDEF -> /* $SELECTED $SELECT -> XEQ . . . -> /* $SELECTED $SELECT PART B:
Now let's add the protocol to your protocol subfile. Again, for the purposes of this example we will use the term "your-protocol-subfile-name" wherever you should substitute the name of your protocol subfile name.
Remember that when PERFORM BUILD PROTOCOLS created your protocols subfile, it also generated a format called $PROTOCOL, automatically used whenever your subfile is selected. When you are adding or updating a protocol record, one of the purposes of the $PROTOCOL format is to add NAME=, the COMMAND=, and the terminating ";" for each line to the commands in the active file so that it conforms to the definition of a protocol record. This formatting occurs automatically when you have selected your protocol subfile and then say ADD or UPDATE.
The only data other than the protocol commands in the active file which you must provide is a * command as the first line of the active file with a name which designates the name for the protocol record. List the protocol you now have in the active file and make sure that the first line meets this requirement, then select your protocol subfile and add the record as follows:
-> LIST FIRST -> SELECT your-protocol-subfile-name -> ADD
PART C:
Now let's try executing the new protocol from the protocol subfile. Clear the active file and try the "..protocol-name" command to execute the protocol by typing in the following:
-> CLEAR ACTIVE -> SELECT your-protocol-subfile-name -> SET XEQ -> ..PROTONAMES
Q1: Did you get a different response than in Part A? Can you explain why?
PART A: Whenever you wish to examine the statements in a protocol, you can display the commands in a protocol at the terminal using the DISPLAY command. -? SELECT your-protocol-subfile-name -? DISPLAY PROTONAMES Notice that the PUBLIC format has stripped off NAME =, COMMAND=, and ";" terminators before displaying each command and placing it in the active file.
PART B: Now let's modify PROTONAMES slightly to obtain both a list of protocols which have been in the protocol subfile for one or more days and a list of those which have been added or updated today. We will first change the FOR GOAL command at line 12 to be a FOR TREE command so that we will recieve only the keys or names of protocols which have been in the protocol subfile for one or more days. We will then insert a new set of commands that will examine only the records in the deferred queue of the subfile. We will also change the various messages in the protocol to reflect the new situation. First select your protocol subfile and transfer PROTONAMES to the active file. Modify the protocol and finally, issue the UPDATE command to replace the protocol in the protocol subfile. The PUBLIC format will strip NAME=, COMMAND= and ";" from the protocol record when you TRANSFER. Similiarly, the PUBLIC format will reinsert these items after you have made your modifications and issued the UPDATE command. -? SELECT your-protocol-subfile-name -? TRANSFER PROTONAMES -? REPLACE 10 LIST 10. ? *PROTOCOL NAME LIST 11. ? *OLD PROTOCOL NAMES -? REPLACE 12 LIST 12. ? FOR GOAL 12. ? FOR TREE -? COLLECT 14.1 LIST 14.1 ? ++DEFQS 14.2 ? * 14.3 ? *PROTOCOLS IN DEFQ 14.4 ? *--------------------------- 14.5 ? FOR DEFQ 14.6 ? SHOW KEYS ALL END="JUMP NODEFQS" 14.7 ? END 14.8 ? (attn) -? REPLACE 20 LIST 20. ? *NO PROTOCOLS CURRENTLY IN SUBFILE 20. ? *NO OLD PROTOCOLS IN SUBFILE -? REPLACE 21 LIST 21 ? JUMP ENDING 21. ? JUMP DEFQS -? COLLECT END 22. ? ++NODEFQS 23. ? *NO PROTOCOL MODIFICATIONS OR ADDITIONS IN DEFQ 24. ? JUMP ENDING 25. ? (attn) -? NUMBER -? LIST 1. ? * PROTONAMES 2. ? !SET NOECHO 3. ? - lists the names of protocols 4. ? - stored in my protocol subfile or deferred queue 5. ? SET MES=0 6. ? LET OLDFILE=" " 7. ? /IF $SELECTED THEN LET OLDFILE=$SELECT 8. ? SELECT your-protocol-subfile-name 9. ? * 10. ? *OLD PROTOCOL NAMES 11. ? *------------------ 12. ? FOR TREE 13. ? SHOW KEYS ALL END="JUMP NORECS" 14. ? END 15. ? ++DEFQS 16. ? * 17. ? *PROTOCOLS IN DEFQ 18. ? *----------------- 19. ? FOR DEFQ 20. ? SHOW KEYS ALL END="JUMP NODEFQS" 21. ? END 22. ? ++ENDING 23. ? /IF #OLDFILE~= " " THEN SELECT #OLDFILE 24. ? SET MES=2 25. ? RETURN 26. ? ++NORECS 27. ? *NO OLD PROTOCOLS IN SUBFILE 28. ? JUMP DEFQS 29. ? ++NODEFQS 30. ? *NO PROTOCOL ADDS OR MODIFICATIONS IN DEFQ 31. ? JUMP ENDING -? XEQ
Q1: Which message(s) did you get when you executed the protocol? Can you explain why you got that particular message rather than one of the others?
PART A: If you are using protocols frequently when you work under SPIRES and also have several SPIRES session defaults which you establish each time you call SPIRES, you may wish to establish an "entry" protocol which is automatically executed each time you call SPIRES. This entry protocol is not stored in your private protocols subfile, but in the system protocol subfile called ENTRY COMMANDS. To add an entry protocol to the ENTRY COMMANDS protocol subfile, you simply collect in the active file the protocol commands you desire to have executed each time you enter SPIRES, then SELECT the ENTRY COMMANDS subfile, and ADD your record. The first command in the active file when you issue the ADD command must be a "*" command containing your account number in the form "gg.uuu". This will be used as the name (or "key") of your protocol record in the ENTRY COMMANDS subfile so that you can display and modify it at some future time. First write down the sequence of commands which you would like to have executed each time you call SPIRES. A possible set of such commands are shown below for the purpose of an example, but you should devise your own set of commands. Notice the account number given in the first line; substitute your own in the form gg.uuu. EXAMPLE : -? SELECT ENTRY COMMANDS -? COLLECT CLEAR 1. ? * GG.LCL 2. ? !SET NOECHO 3. ? SET LENGTH 80 4. ? SET UPPER 5. ? SET VOLUME PUB002 6. ? SELECT MYPROCS 7. ? SET XEQ 8. ? ..PROTONAMES 9. ? RETURN 10. ? (attn) -? ADD Notice that we are invoking a protocol from within a protocol by the use of the ..PROTONAMES command.
The following are error messages that you may receive when you try to compile a vgroup definition.
A variable with a LEN value greater than 253 also has an OCC value greater than one. A variable of that length must be singly occurring.
A LEN value coded for a variable is invalid for the given variable type.
A static variable that was declared singly occurring in its definition has been used with a subscript indicating a multiple occurrence, causing this compilation error.
An invalid type was specified for a static variable.
The total length to be allocated for a variable group is larger than the system maximum. Try defining vgroup arrays with TYPE=DYNAMIC, or splitting the vgroup into two or more vgroups.
The data in the VALUE statement for this variable could not be converted properly for preloading into the variable.
The value to be preloaded into the current variable, specified in the VALUE statement, was either too long or had more occurrences than the OCC value for the variable allows.
An attribute being compiled for this variable has a different number of occurrences than are allowed by the OCC value. For instance, if "OCC = 3,4;", indicating a two-dimensional array, then INDEXED-BY must have two values. If it had one or three values, an error would occur.
The variable name specified on INDEXED-BY must reference a two-byte or four-byte integer variable. For example, if "INDEXED-BY = variablename" then "variablename" must be a two-byte or four-byte integer.
If the OCC statement has more than a single value, indicating a two- or three-dimensional array, then the INDEXED-BY statement must be coded.
A non-string variable may not redefine a string variable, nor can a string variable redefine a non-string variable.
A variable being redefined is itself redefined by another variable. This is acceptable unless the recursive redefinition of variables is greater than five or unless the redefinitions somehow lead back to the original variable. For example, if a variable being redefined is redefining the variable that redefines it, that is not allowed.
INDEXED-BY or redefinition may not be used by variables of type DYNAMIC.
An internal table used to process variables has been filled. The vgroup is too large.
Variables that redefine other variables may not have pre-assigned values. The variable being redefined may have assigned values.
(The following documents are not SPIRES documents per se, but describe utilities and programs that may be useful in developing SPIRES applications.)
The above documents (except any marked "in preparation") may be obtained through the PUBLISH command on the Forsythe computer at Stanford University. If you do not use SPIRES at Stanford, contact your local system administrator to find out how SPIRES documents are made available there.
SPIRES manuals are updated regularly as changes are made to the system. This does not mean that all manuals are out of date with each new version of SPIRES. The changes to the documentation match those made to SPIRES: they are usually minor and/or transparent. Not having the most current version of a manual may mean you do not have all the most recent information about all the latest features, but the information you do have will usually be accurate.
A public subfile, SPIRES DOC NOTES, contains information about changes to SPIRES manuals. Using this subfile, you can determine whether the manual you have has been updated and if so, how significant those updates are. You need to know the date your manual was published, which is printed at the top of each page. For details on the procedure, issue the command SHOW SUBFILE DESCRIPTION SPIRES DOC NOTES.