DocumentationEndless PL/I for OpenVMSReference Manual  ·  ESS-PFVRM-001

Chapter 8

Program Control

The statements described in this chapter direct the run-time flow of execution from statement to statement. They are the DO, BEGIN, END, IF, SELECT, GOTO, LEAVE, STOP, null, ON, SIGNAL, and REVERT statements.

The remainder of the chapter is devoted to handling conditions that could arise during the execution of your program.

8.1. DO Groups and Statements

A DO-group is a sequence of PL/I statements delimited by a DO statement and its corresponding END clause. The statements in a DO-group are executed as the result of an unconditional DO statement or as the result of the successful test of a conditional DO.

For example:

IF A > B THEN DO;
   ⋮
   END;

The statements that occur between the DO and the END are a DO-group. After all statements to be executed in this conditional DO-group are complete, execution continues with the next executable statement following the END statement.

Normally, all the statements contextually nexted one level below the DO in the group are executed. However, control can be transferred out of a DO-group in the following ways:

  • By execution of a GOTO statement that transfers control to a label outside the DO-group. The GOTO statement can be present in the DO-group itself, in a procedure invoked directly or indirectly from within the DO-group, or in an ON-unit activated while the DO-group is active.

  • By execution of a LEAVE statement that transfers control outside the containing DO-group or to the next executable statement following the END statement that terminates the DO-group.

  • By execution of a RETURN or STOP statement that terminates the current procedure or program.

You can nest DO-groups to a maximum level of 64.

The DO statement begins a sequence of statements to be executed in a group; the group ends with the nonexecutable statement END. DO-groups have several formats. These formats are described individually under the following subheadings:

  • Simple DO

  • DO WHILE

  • DO UNTIL

  • Controlled DO

  • DO REPEAT

8.1.1. Simple DO

A simple DO statement is a noniterative DO. The statements nested directly between the DO statement and its corresponding END statement are executed once. PL/I treates these nested statements as if they are one statement. After all statements in the group are executed, control passes to the next executable statement in the program.

The format of a simple DO statement is:

  • DO;

  • .

  • .

  • .

  • END;

Examples

IF A < B THEN DO;
   PUT LIST ('More data needed');
   GET LIST (VALUE);
   A = A + VALUE;
   END;

The simple DO statement is commonly used as the action of the THEN clause of an IF statement, as shown above, or of an ELSE option.

8.1.2. DO WHILE

A DO WHILE statement causes a group of statements to be repeatedly executed as long as a particular condition is satisfied. When the condition is not true, the group is not executed.

The format of the DO WHILE statement is:

  • DO WHILE (test-expression);

  • .

  • .

  • .

  • END;

test-expression

Any expression that yields a scalar value. If any bit of the value is a 1, then the test expression is true; otherwise, the test expression is false. The test expression must be enclosed in parentheses. (Comparison operations yield a value with the type BIT(1).)

This expression is evaluated before each execution of the DO-group. It must have a true value in order for the DO-group to be executed. Otherwise, control passes over the DO-group to the next executable statement following the END statement that terminates the group.

Examples

DO WHILE (A < B);
⋮
END;

This DO-group is executed as long as the value of the variable A is less than the value of the variable B.

DO WHILE (LIST->NEXT ^= NULL());
⋮
END;

This DO-group is executed until a forward pointer in a linked list has a null value.

DECLARE EOF BIT(1) INITIAL('0'B);
   ⋮
ON ENDFILE(INFILE) EOF = '1'B;
READ FILE(INFILE) INTO(INREC);
DO WHILE (^EOF);
   ⋮
   READ FILE(INFILE) INTO(INREC);
   END;

This DO-group reads records from the file INFILE until the end of the file is reached. At the beginning of each iteration of the DO-group, the expression ^EOF is evaluated; the expression is '1'B until the ENDFILE ON-unit sets the value of EOF to '1'B.

8.1.3. DO UNTIL

A DO UNTIL statement causes a group of statements to be repeatedly executed until a particular condition is satisfied. That is, while the condition is false, the group is repeated.

The format of the DO UNTIL statement is:

  • DO UNTIL (test-expression);

  • .

  • .

  • .

  • END;

test-expression

Any expression that yields a scalar value. If any bit of the value is 1, then the test expression is true; otherwise the test expression is false. The test expression must be enclosed in parentheses. (Comparison operations yield a value having the type BIT(1).)

This expression is evaluated after each execution of the DO-group. It must have a false value for the DO-group to be repeated. Otherwise, control passes to the next executable statement following the END statement that terminates the DO-group. The test expression must be enclosed in parentheses.

Note

Both the WHILE and UNTIL options check the status of test expressions, but they differ in that the WHILE option tests the value of the test expression at the beginning of the DO-group, and UNTIL tests the value of the test expression at the end of the DO-group. Therefore, a DO-group with the UNTIL option and no WHILE option will always be executed at least once, but a DO-group with the WHILE option may never be executed.

Examples

DO UNTIL (A=0);
 ⋮
END;

This DO-group is executed at least once and continues as long as the value of A is not equal to zero.

DO UNTIL (K<ALPHA);

 ⋮
END;

This DO-group is executed at least once and continues as long as the value of the variable K is greater than or equal to the value of the variable ALPHA.

8.1.4. Controlled DO

A controlled DO statement identifies a variable whose value controls the execution of the DO-group and defines the conditions under which the control variable is to be modified and repeatedly tested.

The format of the controlled DO statement is:

DO control-variable = start-value
[ TO end-value [BY modify-value] ]
[ BY modify-value                ]
   [ WHILE (test-expression) ]
   [ UNTIL (test-expression) ]
   ;
.
.
.
END;
control-variable

A reference to a variable whose current value, as compared to the end value specified in the TO option, determines whether the DO-group is executed. If none of the options are specified, the DO-group is executed a single time regardless of the value of the control variable. The control variable must be of an arithmetic data type.

start-value

An expression specifying the initial value to be given to the control variable. Evaluation of this expression must yield an arithmetic value.

end-value

An expression giving the value to be compared with the control variable during successive iterations. Evaluation of this expression must yield an arithmetic value. This expression is evaluated exactly once when the statement is executed for the first time. Thus if the end value changes as the loop progresses, only this initial value is used.

modify-value

An expression giving a value by which the control value is to be modified. Evaluation of this expression must yield an arithmetic value. This expression is evaluated exactly once when the statement is executed for the first time. Thus if the modify value changes as the loop progresses, only this initial value is used. If the BY option is not specified, the modify value is 1 by default.

WHILE (test-expression)

An option specifying a condition that further controls the execution of the DO-group. The condition must be true at the beginning of each DO-group iteration for the DO-group to be executed. The specified test expression must yield a scalar value. If any bit in the value is a 1, then the test expression is true; otherwise, the test expression is false. The test expression must be enclosed in parentheses.

UNTIL (test-expression)

An option specifying a condition that further controls the execution of the DO-group. This expression is evaluated at the end of each iteration of the DO-group, before the BY clause is applied to the control variable. The condition must be false at the end of a DO-group execution for the next DO-group iteration to be executed. The specified test expression must yield a scalar value. If any bit in the value is a 1, then the test expression is true; otherwise, the test expression is false. The test expression must be enclosed in parentheses.

Note

If the TO, WHILE, and UNTIL options are omitted, the controlled DO statement specifies no means for terminating the group; the execution of the group must be terminated by a statement or condition occurring within the group.

Example

This DO-group will prompt the user for integer input values, and add each input value to the current sum. When the sum is greater than 100, the DO-group will exit.

DECLARE (NEXT_VALUE,SUM) FIXED BIN;

    SUM = 0;
    DO UNTIL ( SUM > 100 );
        GET LIST (NEXT_VALUE) OPTIONS (PROMPT ('Next value to add? '));
        SUM = SUM + NEXT_VALUE;
    END;

    PUT SKIP LIST ('The total sum is ',SUM);

The controlled DO-group is executed by the following steps:

  1. The following measures are taken to prevent the allocation of a new control variable during the execution of the DO-group:

    • If the control variable is based, its pointer qualifier is evaluated and a temporary reference of the control variable type is created. The temporary reference is used as the control variable in subsequent steps.

    • If the control variable is subscripted, its subscripts are evaluated and a temporary reference of the control variable type is created. The temporary reference is used as the control variable in subsequent steps.

    • If the control variable is neither based nor subscripted, its reference is used in subsequent steps.

  2. The start value expression is evaluated and assigned to the control variable. The expressions specified in the TO and BY options (if specified) are evaluated, and their values are stored. These expressions can contain references to the object referenced by the control variable. If they do, the original reference, not the temporary reference, is used in evaluation of the expressions.

  3. If the TO option is present, the value of the control variable is compared with the end value specified in the TO option. Otherwise, this step is skipped. Execution of the DO-group terminates if either of the following is true:

    • The modify value is greater than zero and the control variable is greater than the end-value.

    • The modify value is less than zero and the control variable is less than the end value.

    If this step terminates the DO-group on the first iteration, the control variable has a final value assigned by the start value. If the group is terminated on a subsequent iteration, the control variable has a final value assigned by step 6.

  4. If a WHILE option is present, its test expression is evaluated. If it does not produce a true value, the execution of the DO-group terminates.

  5. The body of the DO-group is executed. The execution of the DO-group can be terminated during this step by the execution of a STOP or RETURN statement or by the execution of a GOTO or LEAVE statement that transfers control out of the DO-group.

    The body of the DO-group can also contain statements that change the values of the control variable, modify value, end value, or test expression. Changing the modify value or the end value in the body of the loop will not affect the number of times the loop is iterated. However, changing the value of the control variable or the test expression can affect the number of iterations if the control variable was not made into a temporary by step 1.

  6. If an UNTIL option is present, its test expression is evaluated. If it produces a true value, the execution of the DO-group terminates.

  7. Unless none of the options are specified, the value of the control variable is modified as follows:

    control variable = control variable + modify value;
  8. Execution continues at step 3 unless none of the options are specified, in which case control passes to the next executable statement in the program.

Examples

DO I = 2 TO 100 BY 2;

This DO-group is executed 50 times, with values for I of 2, 4, 6, and so on.

DO I = LBOUND(ARRAY,1) TO HBOUND(ARRAY,1);

This DO-group is executed as many times as there are elements in the array variable ARRAY, using the subscript values of the array's elements for the values of I.

DO I = 1 BY 1 WHILE (X < Y);

This DO-group continues to be executed with successively higher values for I while the value of X is less than the value of Y.

DO I = 1 BY 1 WHILE (X < Y) UNTIL (X = 12);

This DO-group resembles the DO-group in the preceding example, except that the DO-group continues to be executed while the value of X is less than the value of Y or until the value of X is equal to 12.

A controlled DO statement that does not specify a TO or BY option results in a single iteration of the following DO-group. For example:

DO X = 1 WHILE (A);

Even if A is true, this DO-group executes a single time only. If A is false, it is not executed at all. Because there is no expression to change the value of X, the DO-group will not be executed again.

DO X = 1;

This DO-group executes a single time only, regardless of the value of X.

8.1.5. DO REPEAT

The DO REPEAT statement executes a DO-group repetitively for different values of a control variable. The control variable is assigned a start value that is used on the first iteration of the group. The REPEAT expression is evaluated before each subsequent iteration, and its result is assigned to the control variable. A WHILE clause can also be included. If it is included, the WHILE expression is evaluated before each iteration (including the first), but after the control variable has been assigned. The format of the DO REPEAT statement is:

DO variable = start-value REPEAT expression
        [WHILE (test-expression)] [UNTIL (test-expression)];
.
.
.
        END;
variable

A reference to a control variable. The control variable can be any scalar variable.

start-value

An expression specifying the initial value to be given to the control variable. The evaluation of this expression must yield a value that is valid for assignment to the control variable.

expression

An expression giving the value to be assigned to the control variable on reiterations of the DO REPEAT group. The expression is evaluated before each iteration after the first. Evaluation of this expression must yield a result that is valid for assignment to the control variable.

WHILE (test-expression)

An option specifying a condition that controls the termination of the DO REPEAT group. The DO REPEAT group continues while the condition is true. The specified test expression must yield a scalar value. If any bit of the value is 1, then the test expression is true; otherwise, the test expression is false. The test expression must be enclosed in parentheses.

This expression is evaluated each time control reaches the DO statement; the test expression must have a true value in order for the DO-group to be executed. Otherwise, control passes to the next executable statement following the END statement that terminates the DO-group.

See Section 8.1.4, “Controlled DO” for a discussion of this option when used with the controlled DO statement.

UNTIL (test-expression)

An option specifying a condition that further controls the termination of the DO REPEAT group. The DO REPEAT group continues until the condition is true. The specified test expression must yield a scalar value. If any bit in the value is 1, then the test expression is true; otherwise, the test expression is false. The test expression must be enclosed in parentheses.

This expression is evaluated after the first execution of the DO-group; the test expression must have a true value in order for the DO-group to be executed a second time. Otherwise, control passes to the next executable statement following the END statement that terminates the DO-group.

Note

If the WHILE and UNTIL options are omitted, the DO REPEAT statement specifies no means for terminating the group; the execution of the group must be terminated by a statement or condition occurring within the group.

A DO REPEAT group is executed by the following steps:

  1. The following measures are taken to prevent the allocation of a new control variable during the execution of the DO-group:

    • If the control variable is based, its pointer qualifier is evaluated and a temporary reference of the control variable type is created. The temporary reference is used as the control variable in subsequent steps.

    • If the control variable is subscripted, its subscripts are evaluated and a temporary reference of the control variable type is created. The temporary reference is used as the control variable in subsequent steps.

    • If the control variable is neither based nor subscripted, its reference is used in subsequent steps.

  2. The start value expression is evaluated and assigned to the control variable.

  3. If a WHILE option is present, its test expression is evaluated. If it does not produce a true value, the execution of the DO-group terminates. If the test expression is not present, execution continues.

  4. The body of the DO-group is executed. The execution of the DO-group may be terminated during this step by the execution of a STOP or RETURN statement or by the execution of a GOTO or LEAVE statement that transfers control outside the DO-group. Statements in the group can also modify the values of the control variable, REPEAT expression, and test expression.

  5. If an UNTIL option is present, its test expression is evaluated. If it produces a true value, the execution of the DO-group terminates. If the test expression is not present, execution continues.

  6. The REPEAT expression is evaluated and its value is assigned to the control variable.

  7. Execution continues at step 3.

Examples

DO LETTER='A' REPEAT (BYTE(I));

This example will repeat the group with an initial LETTER value of 'A' and with subsequent values assigned by the built-in function BYTE(I). The control variable I can be assigned new values within the group. The group will iterate endlessly unless terminated by a statement or condition within the group.

DO I = 1 REPEAT (I + 2) WHILE ( I <= 100 );

This example has the same effect as the following controlled DO statement:

DO I = 1 TO 100 BY 2;

The most common use of the DO REPEAT statement is in the manipulation of lists. For example:

DO P = LIST_HEAD REPEAT (P->LIST.NEXT)
    WHILE ( P ^= NULL() );

In this example, the pointer P is initialized with the value of the pointer control variable LIST_HEAD. The DO-group is then executed with this value of P. The REPEAT option specifies that each time control reaches the DO statement after the first execution of the DO-group, P is to be set to the value of LIST.NEXT in the structure currently pointed to by P.

8.2. BEGIN Statement

The BEGIN statement denotes the start of a begin block.

The format of the BEGIN statement is:

BEGIN;

A begin block is a sequence of statements headed by a BEGIN statement and terminated by an END statement. A begin block can be used wherever a single executable statement is valid, for instance, in an ON-unit. The statements in a begin block can be any PL/I statements, and begin blocks can contain DO-groups, DECLARE statements, procedures, and other (nested) begin blocks.

A begin block provides a convenient way to localize variables. Variables declared as internal within a begin block are not allocated storage until the block is activated. When the block terminates, storage for internal automatic variables is released. A begin block terminates in the following situations:

  • When its corresponding END statement is encountered. Control continues with the next executable statement in the program.

  • When it executes a nonlocal GOTO to transfer control to a previous block.

  • When it executes a RETURN statement.

A begin block differs from a DO-group chiefly in its ability to localize variables. Variables declared within DO-groups are not localized to the group (unless the group contains a begin block or procedure that declares internal variables). Begin blocks are preferable when you want to restrict the scope of variables. Furthermore, there are some cases (such as ON-units) in which DO-groups cannot be used. Otherwise, DO-groups are often more efficient, because they do not have the overhead associated with block activation. In general, you should use a DO-group instead of a begin block unless there are declarations present or you require multiple statements in an ON-unit.

A begin block can designate a series of statements to be executed depending on the success or failure of a test in an IF statement. For example:

IF A = B THEN BEGIN ;
   .
   .
   .
   END;

A begin block also provides the only way to denote a series of statements to be executed when an ON condition is signaled. For example:

ON ERROR BEGIN;
    [statement ...]
    END;

See Section 1.4, “Blocks” for more details on blocks.

8.3. END Statement

The END statement marks the end of the block or group headed by the most recent BEGIN, DO, SELECT, or PROCEDURE statement.

The format of the END statement is:

END [label-reference];
label-reference

A reference to the unsubscripted label on the PROCEDURE, BEGIN, SELECT, or DO statement for which the specified END statement is the termination. A label is not required. If specified, the label reference must match only one label, which is the label of the most recent BEGIN, DO, SELECT, or PROCEDURE statement that is not already matched with an END statement. If the label reference is omitted, the most recent textual, non-terminated PROC, BEGIN, SELECT, or DO statement is matched by default.

Note that a procedure declared with the RETURNS option must execute a RETURN statement before it encounters the END statement marking the end of the procedure.

When the END statement is encountered, one of the following actions is performed, depending on the type of block or group that it terminates:

  • When an END statement denotes the end of a procedure, the procedure is terminated. The storage allocated for the block is released, and all automatic variables are made inaccessible. If the current procedure is the main or only procedure, the program terminates. Otherwise, control returns to the statement following the CALL statement or function invocation that invoked the procedure.

  • When an END statement denotes the end of a begin block, the block is terminated. Storage allocated for the block is released, and all automatic variables are made inaccessible. Control passes to the next executable statement.

  • When an END statement denotes the end of a DO-group, control returns either to the DO statement that heads the group or to the next executable statement following the END statement. If the DO-group is headed by a simple DO, that is, one that causes the DO-group to be executed only once, control passes to the next executable statement. Otherwise, control returns to the head of the DO-group.

  • When an END statement denotes the end of a SELECT-group, the SELECT-group is terminated and control passes to the next executable statement following the end statement.

8.4. IF Statement

The IF statement tests an expression and performs a specified action if the result of the test is true.

The format of the IF statement is:

IF test-expression THEN action [ELSE action];
test-expression

Any valid expression that yields a scalar bit-string value. If any bit of the value is 1, then the test expression is true; otherwise, the test expression is false.

action

Any of the following:

  • Any unlabeled statement except the nonexecutable statements: DECLARE, END, ENTRY, FORMAT, or PROCEDURE

  • An unlabeled DO-group or begin block

The IF statement evaluates the test expression. If the expression is true, the action specified following the keyword THEN is executed. Otherwise, the action, if any, specified following the ELSE keyword is executed.

Examples
IF A < B THEN BEGIN;

The begin block following this statement is executed if the value of the variable A is less than the value of the variable B.

IF ^SUCCESS
THEN
    CALL PRINT_ERROR;
ELSE
    CALL PRINT_SUCCESS;

The IF statement defines the action to be taken if the variable SUCCESS has a false value (the THEN clause) and the action to be taken otherwise (the ELSE clause).

8.4.1. Nested IF Statements

The action specified in a THEN or an ELSE clause can be another IF statement.

An ELSE clause is matched with the nearest preceding IF/THEN that is not itself matched with a preceding ELSE. For example:

IF ABC
THEN
    IF XYZ
    THEN
        GOTO GBH;
    ELSE
        GOTO THESTORE;
ELSE
    GOTO HOME;

In this example, the first ELSE clause is executed if ABC is true and XYZ is false. The second ELSE clause is executed if ABC is false.

In some cases, proper matching of IF and ELSE can require a null statement (a semicolon) as the target of an ELSE. For example:

IF ABC
THEN
    IF XYZ
    THEN
        GOTO HOME;
    ELSE;
ELSE
    GOTO THESTORE;

In this example, the ELSE GOTO THESTORE statement is executed if ABC is false.

8.5. SELECT Statement

The SELECT statement tests a series of expressions and performs a specified action depending on the result of the test. The statement has two forms: in the first form, the expressions in a WHEN clause are tested for truth; in the second form, the expressions in a WHEN clause are compared to see if any have the same value as another specified expression called the select expression. Any of the expressions can be, but need not be, constants. An optional OTHERWISE clause is available to name an action to be performed if none of the preceding expressions have satisfied the condition specified.

The two forms of the SELECT statement and the OTHERWISE clause are described in more detail below.

The general form of the SELECT statement is:

SELECT [(select-expression)];
    [WHEN [ANY|ALL] (case-expression,...) [action] ; ]...
    [{OTHERWISE|OTHER} [action] ; ]
END;
select-expression

An expression that can be evaluated to any type of value.

case-expression,...

One or more expressions to be tested, evaluating to bit-string values, or, if a select expression is used, with values that will be compared to the select expression's value.

action

Any statement (including a null statement, another SELECT statement, a DO-group, or a BEGIN-END block) except a DECLARE, END, ENTRY, FORMAT, or PROCEDURE statement.

8.5.1. The Two Forms of the SELECT Statement

Depending on whether you use a select expression or not, SELECT has two different forms, which are explained in detail below.

SELECT Without a Select Expression

The first form of the SELECT statement omits the select expression. In this form, the expressions in a WHEN clause are evaluated, and a specified action is performed if the result of any test is true (or, if ALL is specified, the results of all tests are true); an expression is true if it evaluates to a bit string containing any bit with the value of '1'B. In the usual case, the test for truth results in a bit string containing one bit: '1'B for true or '0'B for false.

When the keyword ANY (the default) appears in the WHEN clause, then if any one of the expressions evaluates to true the corresponding action is performed. No further expressions in that WHEN clause or in subsequent WHEN clauses are evaluated (and thus the expressions need not have unique values), and no subsequent actions are performed.

The WHEN clauses are checked in the order listed. However, the expressions within one WHEN clause might be evaluated in any order, and not all these expressions are necessarily evaluated. As soon as any expression is found true, subsequent expressions are not evaluated.

If the keyword ALL appears in the WHEN clause, the action is performed only if all expressions in that WHEN clause evaluate to true. Once one action is performed, no subsequent WHEN clauses are evaluated and no subsequent actions are performed. If any expression in the WHEN clause does not result in a true value, no further expressions in that clause are evaluated and the action is not performed.

Following is an example of the first form of SELECT:

SELECT;
    WHEN ANY (A=10,A=20,A=30) B=B+1;
    WHEN (A=50) B=B+2;
    WHEN (A=60) B=B+3;
    WHEN (A=70) B=B+4;
    WHEN (A=80) B=B+5;
    WHEN (A=90) B=B+6;
    WHEN ALL (A>90,A<500) B=B+10;
    OTHERWISE B=B+C;
END;

The SELECT statement defines the action to be taken if the variable A has any of the values specified in the WHEN clauses (or, in the case of the WHEN ALL clause, if A is both greater than 90 and less than 500). If none of the WHEN clauses is true, the action specified in the OTHERWISE clause (B=B+C) is performed.

SELECT With a Select Expression

The second form of the SELECT statement has a select expression after the keyword SELECT. This form of the SELECT statement evaluates expressions in the WHEN clauses and then compares their values to the value of the select expression (instead of testing the expressions for truth or falsity, as in the first form of SELECT). It performs a specified action if any expression has the same value as the select expression (or, if ALL is used, all expressions have the same value as the select expression). In this form of the SELECT statement, as in the previous form, the expressions in a WHEN clause might be evaluated in any order, and not all the expressions are necessarily evaluated.

Following is an example of the second form of SELECT:

SELECT(A);
    WHEN  (50) C=C+1;
    WHEN ANY (60,61,62,B+C) C=C+2;
    WHEN ALL (70,D) C=C+3;
    OTHERWISE C=C+D;
END;

The SELECT statement defines the action to be taken if the select expression (A in the example) evaluates to any or all of the values of the expressions following a WHEN clause. The first action (the assignment statement C=C+1) will be performed if A has a current value of 50. In that case, none of the subsequent WHEN clauses will be evaluated. The second WHEN clause includes the ANY keyword, and so the second action will be performed if A evaluates to or equals 60 or 61 or 62 or the sum of B and C. If neither the first nor the second action is performed, the third WHEN clause's expressions are tested. The third WHEN clause includes the ALL keyword, so the third action will be performed only if A equals both 70 and D. If none of the WHEN clauses causes an action to be performed, then the action in the OTHERWISE clause (the assignment statement C=C+D) will be performed.

8.5.2. OTHERWISE Clause

If none of the WHEN clauses causes the corresponding action to be performed, the action specified in the optional OTHERWISE clause is performed; but if the OTHERWISE clause is omitted, an ERROR condition is signalled. OTHERWISE can be followed by a semicolon (a null statement) to cause execution to continue and avoid an ERROR condition when you do not want to specify an action after OTHERWISE. For example:

OTHERWISE;

After an action is performed following a WHEN or OTHERWISE clause, control passes to the next executable statement following the END statement that terminates the SELECT statement, unless normal flow is altered within the action.

8.5.3. Nested SELECT Statements

Note that the action specified in a WHEN or OTHERWISE clause can be another SELECT statement, resulting in nested SELECT statements, as in the following example:

SELECT;
   WHEN (condition A)
        SELECT;
           WHEN (condition A1) statement 1;
           WHEN (condition A2) statement 2;
           END;
   WHEN (condition B)
        SELECT;
           WHEN (condition B1) statement 3;
           WHEN (condition B2) statement 4;
           OTHERWISE statement 5;
           END;
   OTHERWISE statement 6;
END;

In this example, statement 1 is executed when both condition A and condition A1 are true. Statement 2 is executed when both condition A and condition A2 are true and A1 is false. If A is true but neither A1 nor A2 is true, an ERROR condition is reported because no OTHERWISE clause exists within this SELECT statement.

If condition A is false, condition B is checked. If B is true but B1 and B2 are both false, statement 5 (in the corresponding OTHERWISE clause) is executed. If conditions A and B are both false, statement 6 (in the outermost OTHERWISE clause) is executed.

If you want to avoid the possibility that execution could be stopped by an ERROR condition, which occurs in this example if condition A is true and A1 and A2 are false, you can put in an OTHERWISE clause with a null statement (a semicolon) as its action, which would cause control to pass to the first executable statement following the end of the outermost SELECT statement.

An END statement must terminate each SELECT statement.

8.6. GOTO Statement

The GOTO statement causes control to be transferred to a labeled statement in the current or any outer procedure.

The format of the GOTO statement is:

{ GOTO  } label-reference [OTHERWISE];
{ GO TO }
label-reference

A label constant or an expression that, when evaluated, yields a label value. A label value denotes a statement in the program.

The specified label cannot be the label of an ENTRY, FORMAT, or PROCEDURE statement. The label reference specified in a GOTO statement can be any of the following:

  • An unsubscripted label constant. For example:

         GOTO ALPHA;
            ⋮
    ALPHA:
  • A subscripted label constant, for which the subscript is specified with an integer constant or a variable expression. For example:

         GOTO PROCESS(1);
            ⋮
    PROCESS(1):
  • A label variable that, when evaluated, yields a label value. For example:

    DECLARE PROCESS LABEL VARIABLE;
       ⋮
    PROCESS = BILLING;
       ⋮
    GOTO PROCESS;
  • A subscripted label variable that, when evaluated, yields a label value. For example:

    DECLARE X(5) LABEL;
    X(1) = NEXT;
    
    GOTO X(1);

In the case of a label variable, the resulting label value must designate an existing block activation. (Similarly, a label constant must designate an existing block activation.) If the designated block activation is the current block activation, the GOTO statement causes a local GOTO. No special processing occurs.

OTHERWISE

This option can be used only when the label-reference is a subscripted label with a variable subscript. If present in any other case, it will be reported as an error.

If the variable subscript is out of range and the OTHERWISE option is present, the statement following the GOTO will be executed next. If the OTHERWISE option is not specified and the subscript of the last label is not an asterisk (*), the subscript is reported out of range at run-time and the process will be terminated.

Nonlocal GOTO

If the specified label value is not in the current block, the GOTO statement is considered a nonlocal GOTO. The following can occur:

  • The current block, and any blocks intervening between it and the block containing the label value, are released. This rule applies both to procedure blocks and to begin blocks.

  • If a GOTO statement transfers control out of a procedure that is invoked in a function reference, the statement containing the function reference is not evaluated further.

  • A special case of a nonlocal GOTO occurs if a GOTO is executed in an ON-unit. The condition handling mechanism signals SS$_UNWIND before control transfers to the label. This allows programs to clean up intervening blocks before proceeding.

Examples

The following example shows the use of the GOTO statement:

RESTART:;
  ⋮
  BEGIN;
    ON ERROR GOTO RESTART;
    ⋮
  END;

The GOTO statement provides a transfer address for the current procedure when the ERROR condition is signaled.

DECLARE PROCESS(5) LABEL VARIABLE;
   ⋮
GOTO PROCESS(2);

The GOTO statement evaluates the label reference and transfers control to the label constant corresponding to the second element of the array PROCESS. PROCESS consists of label variables.

The following restrictions apply to the use of labels and label data:

  • No statement can have more than one label. However, an executable statement can be preceded by any number of labeled null statements, which have the same effect as would multiple labels.

  • Operations on label values are restricted to the operators = and ^=, for testing equality or inequality. Two values are equal if they refer to the same statement in the same block activation.

  • Any statement in a PL/I program can be labeled except the following:

    • A DECLARE statement

    • A statement beginning an ON-unit, THEN clause, ELSE clause, WHEN clause, or OTHERWISE clause

  • Labels on PROCEDURE, ENTRY, and FORMAT statements are not considered statement labels and cannot be used as the targets of GOTO statements.

  • An identifier occurring as a label in a block cannot be declared in that block (except as a structure member) or occur in the block's parameter list.

  • Any reference to a label value after its block activation terminates is an error with unpredictable results.

For more information on labels, see Section 3.7, “Label Data”.

8.7. LEAVE Statement

The LEAVE statement causes control to be transferred out of the immediately containing DO-group or out of the containing DO-group whose label is specified with the statement.

The format of the LEAVE statement is:

LEAVE [label-reference];
label-reference

A reference to a label on a DO statement that heads a containing DO-group. The label reference can be a label constant or a subscripted label constant for which the subscript is specified with an integer constant. The label reference cannot be a label variable, nor can it be a subscripted label constant for which the subscript is specified with a variable.

On execution, a LEAVE statement with no label reference causes control to be transferred to the first statement following the END statement that terminates the immediately containing DO-group. If the LEAVE statement has a label, control is passed to the first executable statement following the END statement for the corresponding label indicated in the LEAVE statement. Thus, the LEAVE statement provides an alternative means of terminating execution of a DO-group. In the case of a LEAVE statement with a label reference, several nested DO-groups can be terminated as control transfers outside the referenced DO-group.

Restrictions

The following restrictions apply to the use of the LEAVE statement:

  • A LEAVE statement must be contained within a DO-group.

  • A LEAVE statement must be in the same block as the DO statement to which it refers.

  • If a LEAVE statement has a label reference, it must refer to a label on a DO statement that heads a DO-group that contains the LEAVE statement. The LEAVE statement must be in the same block as the labeled DO statement.

  • The label reference specified with a LEAVE statement must be a label constant or a subscripted label constant with an integer constant subscript.

Examples

The following example shows a LEAVE statement without a label reference:

DO I = 1 TO 100;
   .
   .
   .
    IF COMMAND = 'QUIT' THEN LEAVE;
   .
   .
   .
END;
PUT LIST ('Job finished');

In this example, the LEAVE statement transfers control directly to the PUT statement if the condition in the IF statement is satisfied.

The next example shows a LEAVE statement with a label reference:

LOOP1: DO WHILE (MORE);
   .
   .
   .
    LOOP2: DO I = 1 TO 12;
   .
   .
   .
        IF QUAN(I) > 150 THEN LEAVE LOOP1;
    END;    /* Loop 2 */
   .
   .
   .
END;        /* Loop 1 */

In this example, the LEAVE statement transfers control to the first statement beyond the END statement that terminates LOOP1.

The following examples show some invalid uses of the LEAVE statement:

LEAVE;                     /* LEAVE statement must be in */
                           /* DO-group */

DO;
    BEGIN;
        LEAVE;             /* LEAVE statement must be in */
    END;                   /* same block as DO statement */
END;

ON ENDFILE(SYSIN) LEAVE;   /* ON-unit is separate block */


DECLARE LABVAR LABEL VARIABLE;
LABVAR = LOOP;
LOOP: DO I = 1 TO 10;
    LEAVE LABVAR;          /* Label reference cannot be a variable */
END;

LAB(1): DO;
    LAB(2): DO;
        I = 1;
        LEAVE LAB(I);      /* Subscript must be a constant */
    END;
END;

8.8. STOP Statement

The STOP statement terminates execution of a program, regardless of the current block activation.

The format of the STOP statement is:

STOP;

The STOP statement signals the FINISH condition, and closes all open files. If the main procedure has the RETURNS attribute, no return value is obtainable.

8.9. Null Statement

The null statement performs no action. Its format is:

 ;

The null statement usually serves as the target statement of a THEN or ELSE clause in an IF statement, as the target of a WHEN or OTHERWISE clause in a SELECT statement, or as an action in an ON-unit. The following examples illustrate these uses.

IF A < B THEN GOTO COMPUTE;
     ELSE ;

In this example, no action takes place if A is greater than or equal to B; execution continues at the statement following ELSE ;. A construction of this type may be necessary when IF statements are nested (see Section 8.4.1, “Nested IF Statements”).

SELECT;
    WHEN (condition A,B,C) GOTO FILE_READ;
    WHEN (condition D,E) GOTO UPDATE;
    OTHERWISE;
END;

In this example, control is passed to the next executable statement after END if conditions A, B, C, D, and E are not true.

ON ENDPAGE(SYSPRINT);

In this example, no action takes place upon execution of the ON-unit; the I/O operation that caused the ENDPAGE condition continues.

The null statement can also be used to declare two labels for the same executable statement, as in the following example:

LABEL1: ;
LABEL2: statement ...

8.10. Condition Handling

A PL/I condition is any occurrence that causes the interruption of a program and a signal. When a condition is signaled, PL/I initiates a search for a user-written program unit called an ON-unit to handle the condition.

An ON condition is any one of several named conditions whose occurrence during the execution of a program interrupts the program. When a condition occurs or is signaled, a statement or sequence of statements, called an ON-unit, is executed. The SYSTEM option can be specified in the ON statement, causing the default system condition handling to be executed.

The following list of condition handling topics are discussed in subsequent sections.

  • The ON statement

  • The SIGNAL statement

  • The REVERT statement

  • Summary of ON conditions

  • Default PL/I ON-unit

  • Establishment of ON-units

  • Contents of an ON-unit

  • Search for ON-units

  • Completion of ON-units

8.10.1. ON Statement

The ON statement defines the action to be taken when a specific condition or conditions are signaled during the execution of a program. The ON statement is an executable statement. It must be executed before the statement that signals the specified condition.

The format of the ON statement is:

ON condition-name,... [SNAP]  { on-unit }
                              { SYSTEM; }
condition-name,...

The name or names of the specific conditions for which an ON-unit or the SYSTEM option is specified. There is a keyword name associated with each condition. Successive keyword names must be separated by commas. The conditions are summarized in Table 8.1, “Summary of ON Conditions”; each condition is described in an individual entry in this manual.

SNAP

This option invokes the debugger and causes a traceback of all active routines to be displayed when the condition is raised. If you use the SNAP option, you should specify the /DEBUG qualifier on both the PLI command and the LINK command in order to have all the debugger symbol table information accessible.

If you want to run a program containing the SNAP option in a batch job and cause the program to resume execution after any display of traceback information, you can define DBG$INIT to point to a debug initialization file that contains the following line:

WHILE PC ^= 0 DO(GO)
on-unit

The action to be taken when the specified condition or conditions are signaled. An ON-unit can be:

  • Any single, unlabeled statement except DECLARE, DO, END, ENTRY, FORMAT, IF, ON, PROCEDURE, RETURN, or SELECT.

  • An unlabeled begin block.

  • A null statement (a semicolon alone), which causes program execution to continue as if the condition had been handled.

Only the most recent ON-unit established for a given condition can be active. If two successive ON statements are executed for the same condition, the second ON statement nullifies the first.

If no ON-unit is established for a particular condition, the condition ERROR is signaled. If no ON-unit is established for ERROR condition, the default system ON-unit is executed. See Section 8.10.5, “Default PL/I ON-Unit”.

SYSTEM

This option invokes the default system condition handling for the specified condition, overriding any existing ON-unit for the condition. See Section 8.10.6, “Establishment of ON-Units”.

8.10.2. SIGNAL Statement

The SIGNAL statement causes a specified condition to be signaled, which causes the system to search for and execute an ON-unit to handle the condition. See Section 8.10.8, “Search Path for ON-Units”.

The format of the SIGNAL statement is:

SIGNAL condition-name;
condition-name

The name of the condition to be signaled. It must be one of the keywords listed in Table 8.1, “Summary of ON Conditions”. Each of these conditions is described in its own section.

Most conditions occur as a result of a hardware trap or fault, or as a result of signaling by PL/I run-time procedures. You can use the SIGNAL statement within a program as a general-purpose communication technique. In particular, the VAXCONDITION and CONDITION conditions let you signal unique user-defined condition values.

8.10.3. REVERT Statement

The REVERT statement cancels an ON-unit established for a specified condition or conditions in the current block only.

The format of the REVERT statement is as follows:

REVERT  condition-name,...;
condition-name,...

The keyword name or names associated with the condition or conditions for which the ON-unit is to be reverted. Successive names must be separated by commas. The valid condition names are the same as for the ON statement.

If no ON-unit is established for a specified condition for the current block, the REVERT statement has no effect.

The REVERT statement does not cancel all ON-units that may be active at the same time it is executed (see Section 8.10.8, “Search Path for ON-Units”), only a handler in the current block. If you want to temporarily block activation of all user-written ON-units for a condition, define an ON-unit with the SYSTEM option for the given condition.

An ON-unit can be re-established after execution of a REVERT statement by subsequently executing an ON statement.

The REVERT statement has no effect on an ON-unit established for the ANYCONDITION condition unless the statement explicitly references the ANYCONDITION condition. For example:

REVERT ANYCONDITION;

Therefore, a statement such as the following has no effect on an ON-unit established for the ANYCONDITION condition:

REVERT ZERODIVIDE;

The next example shows this more clearly:

PROGRAM: PROCEDURE OPTIONS(MAIN);
   DECLARE A FIXED;
   DECLARE B FIXED INITIAL (5);
   DECLARE C FIXED INITIAL (0);

   ON ANYCONDITION BEGIN;
      PUT SKIP LIST ('Handled condition = ',ONCODE());
      END;

   REVERT ZERODIVIDE;

   A = B / C;    /* Signal divzero */

   ON ZERODIVIDE BEGIN;
      PUT SKIP LIST ('Handled ZERODIVIDE');
      END;

   A = B / C;    /* Signal divzero */

   REVERT ZERODIVIDE;

   A = B / C;    /* Signal divzero */

   ON ZERODIVIDE BEGIN;
      PUT SKIP LIST ('Handled ZERODIVIDE');
      END;

   REVERT ANYCONDITION;

   A = B / C;    /* Signal divzero */

   REVERT ZERODIVIDE;

   A = B / C;    /* Signal divzero */

END PROGRAM;

When you run the program, the following conditions result:

Handled condition =       1156
Handled ZERODIVIDE
Handled condition =       1156
Handled ZERODIVIDE
PL/I ERROR condition.

8.10.4. Summary of ON Conditions

Most, but not all, ON conditions are associated with errors. The types of conditions for which you can establish ON-units are grouped in the following categories.

  • Conditions that occur during I/O operations:

    • ENDFILE, to take action when the end-of-file occurs while a file is being read

    • ENDPAGE, to take action when the last line on a page is printed

    • KEY, to take action when an error occurs when a record is accessed by key

    • UNDEFINEDFILE, to respond to any file-specific errors that can occur during the opening of a file

  • Conditions that indicate arithmetic conditions related to hardware violations:

    • FIXEDOVERFLOW, to respond when integer or fixed-point values become too large to be expressed

    • OVERFLOW, to respond when floating-point values become too large to be expressed

    • UNDERFLOW (OpenVMS VAX systems only), to respond when floating-point values become too small to be expressed.

    • ZERODIVIDE, to respond when the divisor in a division operation has a value of zero

  • Other conditions:

    • AREA, to respond when an error has been detected during performance of an operation on an area (various subconditions can be determined through use of the ONCODE built-in function)

    • CONDITION, to respond to programmer-defined conditions

    • CONVERSION, to respond to data conversion errors from CHARACTER to any arithmetic data type or bit string

    • STORAGE, to respond when an error has been detected during allocation of a controlled variable or a based variable other than in an area

    • STRINGRANGE, to respond to substring references that are beyond the length of the string.

    • SUBSCRIPTRANGE, to respond to array references with out-of-bound subscripts.

  • General classes of exceptional conditions:

    • ANYCONDITION, to respond to all conditions for which no specific ON-unit is established in the current block

    • ERROR, to respond to language-specific and run-time-specific errors

    • FINISH, to respond when a STOP statement is executed

    • VAXCONDITION, to respond to condition values that are specific to the operating system or to be used as user-defined conditions created by SIGNAL VAXCONDITION(n).

Table 8.1, “Summary of ON Conditions” summarizes ON conditions. Each condition is described individually in the sections that follow.

Table 8.1. Summary of ON Conditions
Condition NameFunction
ANYCONDITIONHandles any condition not specifically handled by another ON-unit
AREAHandles a condition that occurs during an operation on an area
CONDITIONHandles programmer-defined conditions
CONVERSIONHandles data conversion errors
ENDFILEHandles end-of-file for a specified file
ENDPAGEHandles end-of-page for a specified file with PRINT attribute
ERRORHandles miscellaneous error conditions and conditions for which no specific ON-unit exists
FINISHHandles program exit when the main procedure executes a RETURN statement, when any block executes a STOP statement, or when the program exits due to an error that is not handled by an ON-unit
FIXEDOVERFLOWHandles fixed-point decimal and integer overflow exception conditions
KEYHandles any error involving the key during keyed access to a specified file
OVERFLOWHandles floating-point overflow exception conditions
STORAGEHandles a condition that occurs during allocation of a controlled variable or a based variable other than in an area
STRINGRANGEHandles out-of-bound substring references
SUBSCRIPTRANGEHandles out-of-bound array references
UNDEFINEDFILEHandles any errors in opening a specified file
UNDERFLOW (VAX only)Handles floating-point underflow exception conditions
VAXCONDITIONHandles operating system or programmer-specified condition values
ZERODIVIDEHandles divide-by-zero exception conditions

8.10.4.1. ANYCONDITION Condition Name

The ANYCONDITION keyword can be specified in an ON, REVERT, or SIGNAL statement. It designates an ON-unit established for all signaled conditions that are not handled by specific ON-units.

The ANYCONDITION keyword is not defined in the PL/I language. It is provided specifically for use in the OpenVMS operating system environment. For detailed information on OpenVMS condition handling, see the Endless PL/I for OpenVMS User Manual.

8.10.4.2. AREA Condition Name

The AREA condition is raised when various operations fail in relation to areas. For example, it is raised if the extent of an area is not large enough to contain the variable or variables allocated to it, or if the area is incorrectly formatted.

For more information see the Endless PL/I for OpenVMS User Manual.

8.10.4.3. CONDITION Condition Name

The CONDITION condition name is used for ON-units to handle programmer-defined conditions. The value returned by the ONCODE built-in function is PLI$_CONDITION. There is no way to distinguish between multiple programmer-defined conditions if they are specified in the same ON statement.

The format of the CONDITION condition name is:

CONDITION (cond-name)
cond-name

A name declared with the CONDITION attribute.

8.10.4.4. CONVERSION Condition Name

The CONVERSION condition name can be specified in an ON, SIGNAL, or REVERT statement to designate a CONVERSION condition or ON-unit.

PL/I signals the CONVERSION condition when the source character data in a conversion to bit-string or arithmetic data contains characters that are not valid in the specified context. In particular, the CONVERSION condition is raised when a character string is being converted and one of the following conditions is true:

  • The target of the conversion is an arithmetic type, and the source string does not contain a valid, optionally signed arithmetic constant.

  • The target of the conversion is a picture, and the source string does not conform to the picture specification.

  • The target of the conversion is a bit string, and a character other than 0 or 1 appears in the source string.

The CONVERSION condition can be raised either by a non-I/O conversion, such as an explicit conversion using a built-in function or an implicit conversion generated by the compiler, or by an I/O conversion in a GET statement. For example, A = BIT('1014') would cause the CONVERSION condition to be raised, because 4 is not a valid binary digit. Likewise, a GET statement with an arithmetic target would also cause the CONVERSION condition to be raised if the characters '12K45' appeared in the input field, because 'K' is not a valid numeric character.

You can use the ONSOURCE and ONCHAR built-in functions and pseudovariables inside an ON CONVERSION ON-unit. The ONSOURCE built-in function returns the source string that caused the CONVERSION condition to be raised. The ONCHAR built-in function returns the specific character that caused the conversion to fail. You can use the ONSOURCE pseudovariable to change the value of the conversion. Likewise, you can use the ONCHAR pseudovariable to modify only the single character in error.

If the CONVERSION condition was raised during a conversion required by the GET statement, the ONFILE built-in function returns the name of the file constant inside the CONVERSION ON-unit. If the CONVERSION condition was not raised during a conversion required by the GET statement, the ONFILE built-in function returns a null string.

ON-Unit Completion

A normal return from a CONVERSION condition will cause the conversion to be reattempted if the ONSOURCE or ONCHAR pseudovariables have had values assigned to them. If the ONSOURCE value has not been modified, the ERROR condition is raised instead.

For example:

/*
* Sample program that displays a 'quick-fix' CONVERSION
* ON-unit. At the end of this program, TARGET1 contains
* the value 14015, and TARGET2 contains the value '11100'B.
* Note that SOURCE1 and SOURCE2 are not modified.
*/
MAIN: PROCEDURE OPTIONS(MAIN);

    DCL SOURCE1 CHARACTER(5) VARYING INITIAL('14$15');
    DCL SOURCE2 CHARACTER(5) VARYING INITIAL('11q00');

    DCL TARGET1 FIXED BINARY(31);
    DCL TARGET2 BIT(5) ALIGNED;

    /*
    * Sample 'quick-fix' CONVERSION ON-unit that replaces
    * erroneous lowercase q's with 1's, and all other
    * erroneous characters with 0's.
    */
    ON CONVERSION BEGIN;

    PUT SKIP EDIT('"',ONSOURCE(),'" "',ONCHAR(),'"')((5)A);

    IF ONCHAR() = 'q'
    THEN
        ONCHAR() = '1';
    ELSE
        ONCHAR() = '0';

    END; /* ON */

    /*
    * Note that the CONVERSION condition is raised for all
    * 3 of the following statements.
    */
    TARGET1 = SOURCE1;
    TARGET1 = SOURCE1;
    TARGET2 = SOURCE2;

    PUT SKIP(2) EDIT(SOURCE1,SOURCE2)(A,X,A);
    PUT SKIP EDIT(TARGET1,TARGET2)(F(8),X,B(5));

    END MAIN;

The output from this program is:

"14$15" "$"
"14$15" "$"
"11q00" "q"

14$15 11q00
   14015 11100

The target of the conversion is undefined when the CONVERSION condition is raised.

ON-Unit Completion

The retry attempted on a normal return is for the single field that was in error. Attempts to assign a string containing, for example, a comma list of values will not be used for successive data items in a GET statement.

The actual value modified by the ONSOURCE and ONCHAR pseudovariables is a temporary value that is discarded once the conversion is complete, or the control flow cannot return to the point of the error. This means that invalid data stored in a character string variable will cause the CONVERSION condition to be raised each time the value is converted, not just the first time the conversion is attempted, regardless of modifications to the ONSOURCE and ONCHAR pseudovariables inside the CONVERSION ON-unit.

8.10.4.5. ENDFILE Condition Name

The ENDFILE condition name can be specified in an ON, SIGNAL, or REVERT statement to designate an end-of-file condition or ON-unit for a specific file.

PL/I signals the ENDFILE condition when a GET or READ statement attempts an input operation on a file or device after the last data item has been input.

The format of the ENDFILE condition name is:

ENDFILE (file-reference)
file-reference

The name of a file constant or file variable for which the ENDFILE ON-unit is established. If the name of a file variable is specified, the variable must be resolved to the name of a file constant when the condition is signaled.

An ENDFILE ON-unit can be established for any input file. For any particular file, the meaning of the end-of-file condition depends on the type of device. For example, end-of-file is signaled for a terminal device when the Ctrl/Z character is read.

For a stream file, an end-of-file condition is signaled whenever a GET statement attempts to access an empty file or attempts to access a file whose last input field has been read.

For a record file, an end-of-file condition is signaled when a READ statement is executed with the file at the end-of-file position or when a read is attempted beyond the last record in the file. For example:

ON ENDFILE (RECEIPTS) EOF = '1'B;
EOF = '0'B;
OPEN FILE (RECEIPTS) RECORD SEQUENTIAL;
READ FILE (RECEIPTS) INTO (RECORD);
DO WHILE (^EOF);
      .
      .
      .
    READ FILE (RECEIPTS) INTO (RECORD);
    END;

In this example, the ON statement establishes the default action to be taken when the last record in the input file has been processed: the flag EOF is set to '1'B.

An ON-unit established to handle end-of-file conditions can reference the ONFILE built-in function to determine the name of the file constant for which the condition was signaled.

ON-Unit Completion

If the ON-unit for the ENDFILE condition does not transfer control elsewhere in the program, control returns to the statement following the GET or READ statement that caused the condition to be signaled.

When the ENDFILE condition is signaled, it remains in effect until the file is closed. Subsequent GET or READ statements for the file cause the ENDFILE condition to be signaled repeatedly.

8.10.4.6. ENDPAGE Condition Name

The ENDPAGE condition name can be specified in an ON, SIGNAL, or REVERT statement to designate an end-of-page condition or ON-unit for a specific print file.

The format of the ENDPAGE condition name is:

ENDPAGE (file-reference)
file-reference

The name of the file constant or file variable for which the ENDPAGE ON-unit is to be established. If the name of a file variable is specified, the variable must be resolved to the name of a file constant when the condition is signaled. The file must have the PRINT attribute.

The maximum number of lines that can be output on a single page is set by the PAGESIZE option of the OPEN statement. The maximum number of lines allowed on a single page is 32767. If not specified, PL/I determines a default page size using the formula LIB$LP_LINES minus 6.

PL/I signals the ENDPAGE condition when a PUT statement attempts to output a line beyond the last line specified for an output page. When the ENDPAGE condition is signaled, the current line number associated with the file is the page size plus 1. An ENDPAGE ON-unit allows you to provide special processing before output continues on a new page. For example:

ON ENDPAGE (PRINTFILE) BEGIN;
   PUT FILE (PRINTFILE) PAGE;
   PUT FILE (PRINTFILE) LIST(HEADER_LINE);
   PUT FILE (PRINTFILE) SKIP(2);
   END;

The ON-unit for the ENDPAGE condition for the file PRINTFILE outputs a page eject and a header line for the new output page.

To cause PL/I to ignore the ENDPAGE condition when a large amount of output is written to a terminal, you can use the following ON-unit, that contains only the null statement:

ON ENDPAGE(SYSPRINT);

This is optional because PL/I ignores the ENDPAGE condition on SYSPRINT by default. You cannot catch the ENDPAGE(SYSPRINT) condition.

An ON-unit established to handle end-of-page conditions can reference the ONFILE built-in function to determine the name of the file constant for which the condition was signaled.

ON-Unit Completion

If the ON-unit does not transfer control elsewhere in the program, the line number is set to 1 and the program continues execution of the PUT statement. If the ENDPAGE condition was signaled during data transmission, the data is written on the new current line. If the ENDPAGE condition was caused by a LINE or a SKIP option on the PUT statement, then the action specified by these options is ignored on return.

An ENDPAGE condition can occur only once per page of output. If the ON-unit specified does not specify a new page, then execution and output continue. The current line number can increase indefinitely; PL/I does not signal the ENDPAGE condition again. However, if a LINE option on a PUT statement specifies a line number that is less than that of the current line, a new page is output and the current line is set to 1.

Default PL/I Action

If the ENDPAGE condition is signaled during file processing, PL/I starts output on a new page and continues processing. An exception is made for SYSPRINT which is to take no action. If the ENDPAGE condition is signaled as a result of a SIGNAL statement, the statement following the SIGNAL statement is executed and no page is output by default.

8.10.4.7. ERROR Condition Name

The ERROR condition name can be specified in an ON, SIGNAL, or REVERT statement to designate an error condition or ON-unit.

PL/I signals the ERROR condition in the following contexts:

  • When a condition occurs for which the default PL/I action is to signal ERROR

  • When the SIGNAL ERROR statement signals the condition

  • When there is a default PL/I ON-unit and a condition is signaled for which there is no corresponding ON-unit

When any condition is signaled for which no specific ON-unit is established, the default PL/I action for all conditions except ENDPAGE is to signal the ERROR condition.

When any ON-unit is executed, the ON-unit can reference the built-in function ONCODE. This function returns the numeric condition value associated with the specific error that signaled the condition.

ON-Unit Completion

If an ERROR ON-unit does not handle the condition, the program is terminated.

8.10.4.8. FINISH Condition Name

The FINISH condition name can be specified in an ON, SIGNAL, or REVERT statement to designate a FINISH condition or a FINISH ON-unit.

PL/I signals the FINISH condition in the following contexts:

  • When any procedure in the program executes the STOP statement

  • When a procedure that specifies OPTIONS(MAIN) executes a RETURN statement, or, if the procedure does not execute a RETURN statement, when its corresponding END statement is executed

  • When a program exits as a result of an interruption by an external CTRL key function or as a result of a call to the system procedure: SYS$EXIT or SYS$FORCEX (Force Exit)

  • When the SIGNAL FINISH statement signals the condition

ON-Unit Completion

If a FINISH ON-unit that executes as a result of a SIGNAL FINISH statement does not execute a nonlocal GOTO statement, control returns to the statement following SIGNAL FINISH. If the FINISH ON-unit executes as a result of any of the other three causes listed above, the program terminates.

8.10.4.9. FIXEDOVERFLOW Condition Name

The FIXEDOVERFLOW condition name can be specified in an ON, SIGNAL, or REVERT statement to designate a fixed overflow condition or ON-unit.

PL/I signals the FIXEDOVERFLOW condition in the following circumstances:

  • When the result of an arithmetic operation on a fixed-point decimal or binary integer value exceeds the maximum precision of the hardware. The maximum precision allowed for a fixed-point decimal or binary value is 31.

  • When the source value of a fixed-point expression exceeds the precision of the target variable. For example, PL/I signals FIXEDOVERFLOW when a value that is not in the range -128 through 127 is assigned to a fixed-point binary variable with a precision of 7 bits and scale equal to zero. Similarly, the condition is signaled if a value assigned to a picture variable has more integral digits than are specified by the picture specification.

The value resulting from an operation that causes this condition is undefined.

Value of ONCODE

Two hardware exceptions exist that result in the FIXEDOVERFLOW condition. These are SS$_DECOVF (for a fixed-point decimal overflow) and SS$_INTOVF (for a fixed-point binary integer overflow). An ON-unit that receives control when FIXEDOVERFLOW is signaled can reference the ONCODE built-in function to determine which condition is actually signaled.

To define an ON-unit to respond specifically to either of these errors, use the VAXCONDITION condition name.

Example

To respond to a FIXEDOVERFLOW condition caused by either decimal or integer overflow, write an ON-unit as follows:

ON FIXEDOVERFLOW BEGIN;
   IF ONCODE() = SS$_DECOVF THEN DO;   /* Decimal overflow handling */
   END;

   IF ONCODE() - SS$_INTOVF THEN DO;   /* Fixed binary overflow handling */
   END;
END;  /* ON */

To respond to a decimal overflow only, write an ON-unit like the following:

ON VAXCONDITION (SS$_DECOVF) BEGIN;  /* Decimal overflow handling */
END;  /* ON */
ON-Unit Completion

If the ON-unit does not transfer control elsewhere in the program, control returns to the point at which the condition was signaled.

8.10.4.10. KEY Condition Name

The KEY condition name can be specified in an ON, SIGNAL, or REVERT statement to designate a key error condition or ON-unit for a specific file.

The format of the KEY condition name is:

KEY (file-reference)
file-reference

A reference to the file constant or file variable for which the ON-unit is to be established. If the name of a file variable is specified, the variable must be resolved to the name of a file constant when the condition is signaled.

PL/I signals the KEY condition during an operation on a keyed file when an error occurs in processing a key. Some examples of errors for which PL/I signals the KEY condition follow:

  • The record indicated by the specified key cannot be found.

  • The key specification requires conversion from one data type to another and the conversion is not valid.

  • The key is not correctly specified.

  • The key of a relative file exceeds the maximum record number specified when the file was created. This error is shown in the Example section.

An ON-unit established to handle the KEY condition can obtain information about the condition by invoking the following built-in functions:

  • The ONFILE built-in function returns the name of the file being processed when the condition was signaled.

  • The ONCODE built-in function returns the specific RMS condition value associated with the error.

  • The ONKEY built-in function returns the key value that caused the condition to be signaled.

Example

The following example shows the key of a relative file exceeding the maximum record number specified.

%INCLUDE $RMSDEF;

KEYTEST: PROCEDURE OPTIONS(MAIN);

DECLARE
  RECBUF CHAR(80),
  MYFILE FILE;

ON KEY(MYFILE) BEGIN;

  PUT SKIP LIST('Key condition raised');
  IF ONCODE()=RMS$_MRN THEN
    PUT SKIP LIST('You have exceeded the maximum record.');

  STOP;

END;

  OPEN FILE(MYFILE) TITLE('MYFILE.DAT') OUTPUT KEYED
                    ENVIRONMENT(FIXED_LENGTH_RECORDS,
                                MAXIMUM_RECORD_SIZE(80),
                                MAXIMUM_RECORD_NUMBER(20));

  RECBUF = 'This record will not ever make it into the file';

  WRITE FILE(MYFILE) FROM(RECBUF) KEYFROM(100);

END;
ON-Unit Completion

If the ON-unit does not execute a nonlocal GOTO, control returns to the statement immediately following the statement that caused the KEY condition.

8.10.4.11. OVERFLOW Condition Name

The OVERFLOW condition name can be specified in an ON, REVERT, or SIGNAL statement to designate an ON condition or ON-unit for floating-point overflow conditions.

The exponent of a floating-point value is adjusted, if possible, to represent the value with the specified precision. That is, the precision is maximized and the exponent is minimized. The maximum precisions allowed are:

  • OpenVMS VAX systems: binary floating-point value is 113; decimal floating-point value is 34.

  • OpenVMS Alpha systems: binary floating-point value is 53; decimal floating-point value is 15.

PL/I signals the OVERFLOW condition when the result of an arithmetic operation on a floating-point value exceeds the maximum exponent size allowed by the hardware.

The value resulting from an operation that causes this condition is undefined.

ON-Unit Completion

Control returns to the point of the interruption.

8.10.4.12. STORAGE Condition Name

The STORAGE condition is raised when an error has been detected during allocation of a controlled variable or a based variable other than to an area. The ONCODE value is the error returned by LIB$GET_VM. The most common cause is the exhaustion of virtual memory; another cause might be an erroneous attempt to allocate a negative amount of storage.

8.10.4.13. STRINGRANGE Condition Name

The STRINGRANGE condition is raised when a substring reference is beyond the length of the string. The error is detected either by compiled code or by a run-time library routine.

STRINGRANGE can be abbreviated STRG.

Any one of several subconditions can cause the STRINGRANGE condition to be raised. You can use the ONCODE built-in function to determine which one. Following are the possible values of the ONCODE built-in function for the STRINGRANGE condition:

ONCODE valueRaised by
PLI$_STRRANGESIGNAL STRINGRANGE
PLI$_SUBSTR2Out-of-range SUBSTR 2nd argument
PLI$_SUBSTR3Out-of-range SUBSTR 3rd argument
PLI$_BIFSTAPOSOut-of-range starting position for an INDEX, SEARCH, or VERIFY built-in function

Note that STRINGRANGE is always enabled in RTL code (which is currently used for more complex cases of INDEX, SEARCH, and VERIFY), but in-line checking is only performed if /CHECK=BOUNDS is used to compile the code in which the condition would be raised.

An example of the use of the STRINGRANGE condition and the ONCODE built-in function follows.

%INCLUDE $PLIDEF;
ON STRINGRANGE BEGIN;
            /*
             * The THEN clause below will be executed for all
             * SUBSTR starting-position range errors. All other
             * STRINGRANGE errors will be resignaled. Note that
             * SUBSTR is processed in-line, so the code must be
             * compiled with /CHECK=BOUNDS for this ON-unit to
             * be effective.
             */
            IF ONCODE() = PLI$_SUBSTR2
            THEN
                ⋮
            ELSE
                CALL RESIGNAL();
            END;

8.10.4.14. SUBSCRIPTRANGE Condition Name

The SUBSCRIPTRANGE condition is raised in response to out-of-bounds subscripts in references to arrays. The value returned by the ONCODE built-in function for the SUBSCRIPTRANGE condition is PLI$_SUBRANGE or PLI$_SUBRANGEn, where n is the number of the subscript, in the range 1 through 8.

8.10.4.15. UNDEFINEDFILE Condition Name

The UNDEFINEDFILE condition name can be specified in an ON, SIGNAL, or REVERT statement to designate an undefined file condition or ON-unit for a specific file.

The format of the UNDEFINEDFILE condition name is:

{ UNDEFINEDFILE } (file-reference)
{ UNDF          }
file-reference

A reference to a file constant or file variable for which the ON-unit is established.

If the name of a file variable is specified, the variable must be resolved to the name of a file constant when the condition is signaled.

PL/I signals the UNDEFINEDFILE condition when a file cannot be opened. Following are some examples of errors that cause the UNDEFINEDFILE condition:

  • The value specified by the TITLE option is an invalid file specification.

  • The file is opened for input or update and the specified file does not exist.

  • An existing file is accessed with PL/I file description attributes that are inconsistent with the file's actual organization.

  • Any system-detected file error prevents the file from being accessed.

The UNDEFINEDFILE condition lets you establish an ON-unit to provide processing when a file cannot be opened, for example, to provide a default file if no file is specified at run time.

X: PROCEDURE (FILENAME);
DECLARE FILENAME CHARACTER (128) VARYING;
DECLARE INPUT_FILE FILE INPUT;
    ON UNDEFINEDFILE (INPUT_FILE)
           OPEN FILE (INPUT_FILE)
           TITLE ('SYS$INPUT');
    OPEN FILE (INPUT_FILE) TITLE (FILENAME);

In this example, the procedure X expects a file specification string to be passed as an argument. If no argument is passed, or if the argument is not a valid file specification, the OPEN statement fails. The UNDEFINEDFILE ON-unit provides a default OPEN statement with the file specification SYS$INPUT.

An ON-unit established to handle the UNDEFINEDFILE condition can obtain information about the condition by invoking the following built-in functions:

  • The ONFILE built-in function returns the name of the variable defined as FILE that was being processed when the condition was signaled.

  • The ONCODE built-in function returns the specific status value associated with the error.

ON-Unit Completion

The action taken on a normal return from the UNDEFINEDFILE condition depends on whether the file was opened explicitly or implicitly.

If the UNDEFINEDFILE condition was signaled following an explicit OPEN statement for a file, then the normal action following the ON-unit execution is for the program to continue. If the ON-unit does not transfer control elsewhere in the program, control returns to the statement following the OPEN statement that caused the condition to be signaled.

If the UNDEFINEDFILE condition was signaled during an implicit open attempt, the run-time system tests the state of the file. If the file is not open, the ERROR condition is signaled. If the file was opened by the ON-unit, execution of the I/O statement continues.

If an ON-unit receives control when an explicit OPEN results in the UNDEFINEDFILE condition, and the ON-unit does not handle the condition by opening the file or by transferring control elsewhere in the program, control returns to the statement following the OPEN. Then, if an attempt is made to access the file with an I/O statement, the UNDEFINEDFILE condition is signaled again when PL/I attempts the implicit open of the file. This time, PL/I signals the ERROR condition on completion of the ON-unit.

8.10.4.16. UNDERFLOW Condition Name (Endless PL/I V3.9 for OpenVMS VAX only)

On Endless PL/I V3.9 for OpenVMS VAX you can specify the UNDERFLOW condition name (which can be abbreviated UFL) in an ON, REVERT, or SIGNAL statement to designate a floating-point underflow condition or ON-unit. The Alpha hardware does not support UNDERFLOW detection; therefore, the underflow condition is not raised on OpenVMS Alpha systems.

Endless PL/I V3.9 for OpenVMS VAX signals the UNDERFLOW condition when the absolute value of the result of an arithmetic operation on a floating-point value is smaller than the minimum value that can be represented by the VAX hardware.

ON-Unit Completion

On completion of the ON-unit, control is returned to the point of the interrupt. Continued execution is unpredictable.

This condition is signaled by Endless PL/I V3.9 for OpenVMS VAX only in procedures in which the UNDERFLOW option is enabled. The option is enabled when you specify UNDERFLOW in the procedure options. Endless PL/I V5.0 for OpenVMS Alpha ignores the UNDERFLOW option.

The value resulting from an operation that causes the UNDERFLOW condition is undefined. (The value would be set to zero only if UNDERFLOW were not specified in the procedure options.)

8.10.4.17. VAXCONDITION Condition Name

The VAXCONDITION condition name can be specified in an ON, RESIGNAL, REVERT, or SIGNAL statement. The VAXCONDITION condition name provides a way to signal and handle operating-system or programmer-specified condition values. The format of the VAXCONDITION condition name is:

VAXCONDITION (expression)
expression

An expression yielding a fixed binary value. The expression is evaluated when the ON statement is executed, not when the condition is signaled.

The VAXCONDITION condition name is provided specifically for PL/I procedures that interact with operating system routines. For details on using the VAXCONDITION condition name and the meanings of system- and user-defined values that you can specify, see the Endless PL/I for OpenVMS User Manual.

8.10.4.18. ZERODIVIDE Condition Name

The ZERODIVIDE condition name can be specified in an ON, REVERT, or SIGNAL statement to designate a divide-by-zero condition or ON-unit.

PL/I signals the ZERODIVIDE condition when the divisor in a division operation has a value of zero. The value resulting from such an operation is undefined.

8.10.5. Default PL/I ON-Unit

PL/I defines a default ON-unit for the procedure that is designated as the main procedure. This is why there must be exactly one procedure with OPTIONS(MAIN) specified in any executable image. This default ON-unit performs the following actions depending on the condition signaled. Note that the severity of the signal is determined by the low three bits of the condition code.

  • If the signal is the ENDPAGE condition, the default PL/I handler executes a PUT PAGE for the file, and then continues the program at the point at which ENDPAGE was signaled. Note that ENDPAGE is ignored by default for SYSPRINT (see Section 8.10.4.6, “ENDPAGE Condition Name” for more information).

  • If the signal is the ERROR condition and the severity is fatal, the default handler signals the FINISH condition. Then, one of the following occurs:

    • If a FINISH ON-unit is found, it is given a chance to execute. If it executes a nonlocal GOTO or if it signals another condition, program execution continues.

    • If no FINISH ON-unit is found or if a FINISH ON-unit completes execution by handling the condition, then PL/I resignals the condition to the default condition handler. This handler prints a message, displays a traceback, and terminates the program.

  • If the signal is any condition other than ENDPAGE or ERROR with a fatal severity, the default PL/I handler signals the ERROR condition with the severity of the original condition. Then, one of the following occurs:

    • If an ERROR ON-unit is found, it is executed. If it completes execution by handling the condition, the program continues.

    • If an ERROR ON-unit is not found, the default PL/I handler resignals the condition. If this resignal results in return of control to the system, the default condition handler prints a message and a traceback. If the error is a fatal error, the default handler terminates the program; if the error is nonfatal, the program continues.

8.10.6. Establishment of ON-Units

An ON-unit is established for a specific ON condition or conditions following the execution of an ON statement that specifies the condition name(s). For example:

ON ENDFILE (ACCOUNTS) GOTO CLOSE_FILES;

This ON statement defines an ON-unit for an ENDFILE (end-of-file) condition in the file specified by the name ACCOUNTS. The ON-unit consists of a single statement, a GOTO statement.

After an ON-unit is established by an ON statement for a condition, it remains in effect for the activation of the current block and all its dynamically descendant blocks, unless one of the following occurs:

  • Another ON statement is specified for the same condition in a dynamically descendant block. The ON-unit established within the descendant block remains in effect as long as the descendant block is active.

  • A REVERT statement is executed for the specified condition. A REVERT statement nullifies the most recent ON-unit for the specified condition. (See Section 8.10.3, “REVERT Statement”).

  • Another ON statement is specified for the same condition within the current block. Within the same block, an ON statement for a specific condition cancels the previous ON-unit.

  • The block or procedure within which the ON-unit is established terminates. When a block exits, any ON-units it has established are reverted.

  • For an ON-unit is established inside the ON-unit:

    ON OVERFLOW BEGIN
       ⋮
       ON OVERFLOW BEGIN
       ⋮
       END;
    END;

8.10.7. Contents of an ON-Unit

An ON-unit can consist of a single simple statement, a group of statements in a begin block, or a null statement.

Simple Statements in ON-Units

The following ON statement specifies a single statement in the ON-unit:

ON ERROR GOTO WRITE_ERROR_MESSAGE;

This ON statement specifies a GOTO statement that transfers control to the label WRITE_ERROR_MESSAGE in the event of the ERROR condition.

A simple statement must not be labeled and must not be any of the following:

DECLAREFORMATRETURN
DOIFSELECT
ENDON 
ENTRYPROCEDURE 
Begin Blocks in ON-Units

An ON-unit can also consist of a sequence of statements in a begin block. For example:

ON ENDFILE (SYSIN) BEGIN;
   CLOSE FILE (TEMP);
   CALL PRINT_STATISTICS(TEMP);
   END;

This ON-unit consists of CLOSE and CALL statements that request special processing when the end-of-file condition occurs during reading of the default system input file, SYSIN.

If a BEGIN statement is specified for the ON-unit, the BEGIN statement must not be labeled. The begin block can contain any statement except a RETURN statement.

Null Statements in ON-Units

A null statement specified for an ON-unit indicates that no processing is to occur when the condition occurs. Program execution continues as if the condition had been handled. For example:

ON ENDPAGE(SYSPRINT);

This ON-unit causes PL/I to continue output on a terminal regardless of the number of lines that have been output.

8.10.8. Search Path for ON-Units

When a condition is signaled during the execution of a PL/I procedure, PL/I searches for an ON-unit to respond to the condition. This occurs unless you have used the SYSTEM option in an ON statement for the condition; the SYSTEM option causes the system default action to be executed regardless of the existence of any ON-unit.) PL/I first searches the current block, that is, the block in which the condition occurred. If no ON-unit exists in this block for the specific condition, it searches the block that activated the current block (its parent), and then the block that activated that block, and so on.

PL/I executes the first ON-unit it finds, if any, that can handle the specified condition. If no ON-unit for the specific condition is found, default PL/I condition handling is performed.

8.10.9. Completion of ON-Units

The ON-unit can complete its execution in any of the following ways:

  • If the ON-unit executes a nonlocal GOTO statement, or if it invokes a subroutine or function that executes a nonlocal GOTO, program control is transferred to that statement and continues sequentially at that point in the program.

  • If the ON-unit executes a STOP statement, then the FINISH condition is signaled. If no FINISH ON-unit exists, the program is terminated.

  • An ON-unit can use the RESIGNAL built-in subroutine to request that PL/I continue to search for an ON-unit to handle a specific condition.

  • When any ON-unit (except for ERROR or FINISH) completes normally, control returns either to the statement that caused the condition or to the statement immediately following the statement that caused the condition.

Descriptions of each ON condition in this manual indicate the action that PL/I takes on completion of an ON-unit associated with the condition.