DocumentationEndless PL/I for POSIX SystemsUser's Manual  ·  ESS-PFUUM-001

Chapter 9

Error Handling

In PL/I, errors are signaled through ON conditions and handled by groups of statements called ON-units. An ON condition is any one of several named conditions that interrupt program execution. When an ON condition occurs, or is signaled, the corresponding ON-unit is executed.

This chapter discusses the following topics:

  • The RESIGNAL built-in subroutine

  • The VAXCONDITION condition

  • The actions that ON-units can take

  • The relationship of UNIX condition handling to PL/I condition handling

  • The search for ON-units when a condition is signaled, and the default handling performed when no ON-unit exists

  • The scope of ON-units

  • Some examples of ON-units

  • The condition-handling built-in functions (ONCODE, and ONFILE)

Refer to the Endless PL/I for POSIX Systems Reference Manual for descriptions of the ON, REVERT, and SIGNAL statements and ON conditions.

9.1. RESIGNAL Built-In Subroutine

The RESIGNAL built-in subroutine is used in an ON-unit to pass a signaled condition, so that the run-time system will attempt to locate another ON-unit to handle the condition.

RESIGNAL works by setting up the internal mechanism for passing the signal. It does not by itself cause an exit from the ON-unit that calls it. Instead, it returns to the next statement in the ON-unit. Resignaling does not occur until execution of the ON-unit is completed.

The format of a statement calling the RESIGNAL built-in subroutine is:

CALL RESIGNAL();

When an ON-unit has determined that it cannot or should not respond to a condition, RESIGNAL permits the ON-unit to pass the signal along.

This subroutine is not part of the standard PL/I language. It is provided specifically for use in the UNIX operating system environment.

9.2. VAXCONDITION Condition

VAXCONDITION provides a general-purpose signaling mechanism for PL/I applications that execute under the control of the VMS operating system. This condition is carried to the PL/I compiler for compatibility reasons.

An ON-unit for a VAXCONDITION condition is established in the following format:

ON VAXCONDITION (expression) on-unit
expression

Any expression yielding a FIXED BINARY(31) value that can represent either a specific VMS condition value or status value, or an arbitrary user-specified value. The low-order three bits of the value are interpreted as the condition's severity by the default PL/I and system condition handlers. The expression is evaluated when the ON statement is executed, not when the condition is signaled.

A VAXCONDITION ON-unit can be used in conjunction with calls to system service, run-time library, or other system procedures that return status values. An error-handling routine can test the return status value from a system procedure and, if not successful, signal the error.

ON-units can exist for any number of specific values. For example:

VCND: PROCEDURE OPTIONS(MAIN);

%REPLACE COND1 BY 1234;
%REPLACE COND2 BY 5678;
ON VAXCONDITION(COND1), VAXCONDITION(COND2)
BEGIN;
       PUT SKIP LIST ('Main handler for VAXCONDITIONs: ',
                       COND1,COND2);
       PUT SKIP EDIT (' Caught ', oncode()) (A,F(5,0));
END;

P1: PROCEDURE;

ON VAXCONDITION(COND1)
BEGIN;
       PUT SKIP EDIT ('p1: caught VAXCONDITION: ',COND1)
                       (A,F(5,0));
       CALL RESIGNAL ();
END;

SIGNAL VAXCONDITION(COND1);

END P1;

P2: PROCEDURE;

ON VAXCONDITION(COND2)
BEGIN;
       PUT SKIP EDIT ('p2: caught VAXCONDITION: ',COND2)
                       (A,F(5,0));
       CALL RESIGNAL ();
END;

       SIGNAL VAXCONDITION(COND2);

END P2;

       CALL P1;
       CALL P2;
       CALL P1;

END VCND;

9.3. ON-Unit Actions

During its execution, an ON-unit can take any of the following courses of action:

  • Handle the condition and return control to the point at which the condition was signaled

  • Resignal the condition and request PL/I to locate another ON-unit to handle it

  • Execute a nonlocal GOTO statement and cause PL/I to unwind the call stack

  • Stop the program

These courses of action are described individually in the following subsections.

9.3.1. Handling the Condition

A condition is assumed to be handled in PL/I when the ON-unit established for the condition completes execution without performing one of the following actions:

  • Executing a nonlocal GOTO

  • Calling the RESIGNAL built-in subroutine

  • Signaling another condition

  • Executing a STOP statement

When the condition is handled, PL/I continues execution of the program at the point of interruption. Normal completion of any ON-unit (except ERROR signaled as the default action) results in return of control either to the statement that caused the condition or to the statement immediately following the statement that caused the condition. However, the effects of normal return from ERROR, FIXEDOVERFLOW, OVERFLOW, UNDERFLOW, and ZERODIVIDE are generally unpredictable. Exceptions are cases of ERROR that are specifically documented to allow normal return, and ON-units that execute as a result of a SIGNAL statement. Normal return from a CONVERSION condition that modifies ONCHAR or ONSOURCE pseudovariables causes the conversion to be retried.

9.3.2. Resignaling the Condition

In PL/I, an ON-unit can choose not to handle a condition and can request that, rather than returning control to the point of interruption, PL/I continue to search for another ON-unit to handle the condition. It does this by calling the RESIGNAL built-in subroutine as follows:

CALL RESIGNAL();

The RESIGNAL built-in subroutine has no arguments.

When an ON-unit calls RESIGNAL, PL/I resumes its search of the call stack starting at the call frame beneath the frame in which it located the current ON-unit. The next example and Figure 9.1, “Resignaling a Condition” illustrate the effect of the RESIGNAL built-in subroutine. The reverse numbers indicate the order of execution. Procedure B establishes an ANYCONDITION ON-unit that handles specific VAXCONDITION values. When the ON-unit is executed as a result of a signal in either procedure B or C, it tests the current value of ONCODE to see whether it is a value of interest. If not, procedure B calls RESIGNAL.

A: PROCEDURE OPTIONS(MAIN); ❶
       ON FIXEDOVERFLOW BEGIN;

       ⋮
       END;

       CALL B;
B: PROCEDURE (X,Y);  ❷
       ON ANYCONDITION BEGIN;
               IF (ONCODE() = SIGNAL_FOUND)!
                       (ONCODE() = SIGNAL_DONE) THEN
                       BEGIN;

               END; /*Begin block for IF statement */
               ELSE CALL RESIGNAL();
       END; /* ON-unit */
       CALL C;
C: PROCEDURE;  ❸

       ⋮/* FIXEDOVERFLOW signaled  ❹
       RETURN;
       END;

Figure 9.1, “Resignaling a Condition” illustrates the preceding example.

Figure 9.1. Resignaling a Condition
Resignaling a Condition A B C

When the FIXEDOVERFLOW condition is signaled in procedure C, B calls RESIGNAL and PL/I continues its search of the call stack. It locates the ON-unit for handling the FIXEDOVERFLOW condition in procedure A and executes it.

Note that the default condition handling performed by PL/I uses the resignaling capability to continue signals that are not handled within the program. PL/I default condition handling is described in Section 9.5, “Search Path for ON-Units”.

9.3.3. Executing a Nonlocal GOTO

An ON-unit in a PL/I procedure can execute a nonlocal GOTO statement that transfers control to a previous block. In this case, PL/I releases call frames, beginning with the call frame created for the ON-unit, until it reaches the block containing the label specified in the GOTO statement.

This removal of call frames from the call stack is called an unwind. The next example and Figure 9.2, “Unwinding the Call Stack” illustrates a situation in which an unwind occurs. The reverse numbers indicate the order of execution. The ERROR ON-unit established in procedure A receives control when the ERROR condition is signaled in procedure C. This ON-unit executes the GOTO PRINT_MSG statement. The label PRINT_MSG is in procedure A. Thus, the call stack is unwound and the call frames for the ON-unit, procedure C, and procedure B, in that order, are removed from the stack, and execution continues at the label PRINT_MSG.

A: PROCEDURE OPTIONS(MAIN); ❶
       ON ERROR GOTO PRINT_MSG;

       ⋮  ❹
       CALL B;
       PRINT_MSG;

       ⋮
B: PROCEDURE;  ❷

       CALL C;

       RETURN;
       END;
C: PROCEDURE;  ❸

       RETURN; /* ERROR signaled */
END;

Figure 9.2, “Unwinding the Call Stack” illustrates the preceding example.

Figure 9.2. Unwinding the Call Stack
Unwinding the Call Stack A B C

When an unwind occurs in the UNIX environment, PL/I examines each call frame in the calling sequence to determine if a condition ON-unit exists for that frame. If so, PL/I calls the ON-unit with the condition value SS$_UNWIND, and the ON-unit has the chance to perform block- or procedure-specific cleanup operations.

9.3.4. Stopping the Program

You can use an ON-unit to terminate program execution. To do this, include a STOP statement in the ON-unit. For example:

ON UNDEFINEDFILE(INFILE) BEGIN;

       PUT EDIT('File ',ONFILE(),' undefined. Error ',ONCODE())
                       (A,X,A,X,A,X,F(10));
       STOP;
END;

The STOP statement performs the following actions:

  • It signals the FINISH condition.

  • It calls the exit(3) system library routine to perform an image exit.

Thus, when a FINISH ON-unit that has been executed as a result of a STOP statement handles the condition, control returns to the STOP statement, which then terminates the image.

If no FINISH or ANYCONDITION ON-unit exists, the program terminates.

Important

If a FINISH or ANYCONDITION ON-unit executes a STOP statement, the program enters an infinite loop. Therefore, do not put STOP statements in FINISH or ANYCONDITION ON-units.

9.4. Relationship of UNIX Signals to PL/I ON-Units

In the UNIX environment, an exception is a hardware- or software-detected condition that synchronously or asynchronously interrupts the execution of an image. A condition handler is a procedure that exists specifically to respond to one or more such conditions; each procedure in the program can establish a condition handler. It is usually the responsibility of each handler to determine the specific condition that was signaled, and to decide whether or not to handle it.

Most high-level languages do not provide language constructs to handle exceptions; therefore, condition handlers are established by calling the UNIX Run-Time Library procedure signal(3). The PL/I language, however, uses the ON-unit condition handler to handle a specific condition. By using the keyword condition names defined by PL/I and the extensions provided by PL/I, you can write ON-units to handle any possible UNIX-specific condition. Each procedure can establish separate ON-units for each of several possible conditions that the procedure wishes to handle.

Important

You should never use signal(3) to establish a condition handler in a PL/I call frame.

UNIX signals are mapped to PL/I signals and can be handled by on units in PL/I. The main program establishes default handlers for UNIX signals, thus this behavior is only in affect when the main program is in PL/I. PL/I runtime library establishes the default signal handler for all UNIX signals except SIGSTOP and SIGKILL which cannot be caught. For more information about UNIX signals see the signal(3) manpage.

The default handling of signals is as follows:

  1. This signal is translated into an appropriate ONCODE. Refer to Table 9.1, “Corresponding Values” for mapping from signal to oncode values.

  2. The normal condition handling mechanism is used to identify the appropriate PL/I condition handler to be invoked. That is, the stack is searched for an on unit that handles the condition. Floating point exceptions that are generated by hardware conditions are translated one of the following conditions:

    OVERFLOW

    UNDERFLOW

    ZERODIVIDE

    All other signals generated are handled either by a VAXCONDITION(signal) handler for the specific signal code or by the PL/I ANYCONDITION handler. As usual the specific code that caused the condition is available by calling ONCODE().

  3. If there is no ON-unit condition handler for the signal, the default action is the default action as defined by the operating system. This is described in signal(3).

    Note that unlike other conditions, in the case of a UNIX signal not handled by a specific on condition handler, a PL/I ERROR condition is NOT generated. This allows UNIX signals to behave in the normal way if no condition handlers are used.

9.4.1. Accessing the Signal Values

Within a PL/I program the values for the UNIX signals are available in the following variables, each corresponding to one of the UNIX signals. For access to these variables, a program can GLOBALREF the variables. The standard include file, signaldef.in, contains the declarations for these variables. To include the signaldef.in file in your PL/I program, use the following statement:

%include $signaldef;

Table 9.1, “Corresponding Values” identifies the correspondence between UNIX signal numbers and the corresponding ONCODE value.

Table 9.1. Corresponding Values
SignalVariableInitial Value
DecimalHexadecimal
SIGHUPsig$_hup1401159800x85a000c
SIGINTsig$_int16170x651
SIGQUITsig$_quit1401159960x85a001c
SIGILLsig$_ill1401160040x85a0024
SIGTRAPsig$_trap1401160120x85a002c
SIGIOTsig$_iot1401160200x85a0034
SIGEMTsig$_emt1401160280x85a003c
SIGFPEsig$_fpe1401160360x85a0044
SIGBUSsig$_bus1401160520x85a0054
SIGSEGVsig$_segv120xc
SIGSYSsig$_sys1401160680x85a0064
SIGPIPEsig$_pipe1401160760x85a006c
SIGALRMsig$_alrm1401160840x85a0074
SIGTERMsig$_term1401160920x85a007c
SIGURGsig$_urg1401160980x85a0082
SIGTSTPsig$_tstp1401161140x85a0092
SIGCONTsig$_cont1401161220x85a009a
SIGCHLDsig$_chld1401161300x85a00a2
SIGTTINsig$_ttin1401161380x85a00aa
SIGTTOUsig$_ttou1401161460x85a00b2
SIGIOsig$_io1401161540x85a00ba
SIGXCPUsig$_xcpu1401161640x85a00c4
SIGXFSZsig$_xfsz1401161720x85a00cc
SIGVTALRMsig$_vtalrm1401161800x85a00d4
SIGPROFsig$_prof1401161880x85a00dc
SIGWINCHsig$_winch1401161940x85a00e2
SIGUSR1sig$_usr11401162120x85a00f4
SIGUSR2sig$_usr21401162200x85a00fc
SIGTRAPss$_intdiv11560x484
SIGTRAPss$_intovf11480x47c
SIGTRAPss$_subrng11960x4ac
SIGFPEss$_fltovf11640x48c
SIGFPEss$_decovf11880x4a4
SIGFPEss$_fltdiv11720x494
SIGFPEss$_fltund11800x49c

9.4.2. Modifying the ONCODE Values

The numbers listed in Table 9.1, “Corresponding Values” for the ONCODE values are under program control. The values listed are the initial value for each of the signals. These numbers may be modified, if desired, by assigning a new value to the variable. Take care not to duplicate these signal numbers or they cannot be distinguished. These values should not be modified within a condition handler.

You may want to use your own number for the ONCODE value, if the numbers are used to identify a related error message.

The following program shows an example of how UNIX signals can be handled, and how the ONCODE values can be modified:

UNIXSIG: PROC OPTIONS( MAIN );

dcl sig$_usr1 fixed bin(31) globalref;
dcl sig$_int fixed bin(31) globalref;
dcl sig$_quit fixed bin(31) globalref;
/* set specific value for sig$_int */
sig$_int = 999999;

call mainloop();

/* set up anycondition handler to test for
* sig$_usr1, sig$_int and sig$_quit */
mainloop: proc;
on anycondition begin;
       if (oncode() = sig$_usr1) then
               PUT LIST ( 'mainloop: caught sigusr1 ',oncode());
       else if oncode() = sig$_int then
               PUT LIST ( 'mainloop: caught sigint ',oncode());
       else if oncode() = sig$_quit then
               PUT LIST ( 'mainloop: caught sigquit ',oncode());
       else call resignal();
       put skip;
end;

/* set up specific handler for sig$_usr2 */
on vaxcondition(sig$_usr2) begin;
       PUT LIST ( 'usr2: caught sigusr2 ',oncode());
       put skip;
end;

/* main loop processing starts here */
⋮
END;

In this example, specific handlers are set up for the UNIX signals SIGUSR1, SIGINT, SIGQUIT, and SIGUSR2. The first three are caught in the ANYCONDITION handler and the ONCODE is compared to the current values in the global variables sig$_usr1, sig$_int and sig$_quit. The signal SIGUSR2 is caught in a specific VAXCONDITION on units testing for sig$_usr2.

9.5. Search Path for ON-Units

In PL/I, each call frame in the calling sequence for ON-units is searched in a specific sequence. If it reaches the call frame at which the program was entered without locating an ON-unit, it performs default condition handling. The default handling depends on whether or not the call frame at which the procedure was entered specified the MAIN option.

The next example and Figure 9.3, “Execution of an ON-Unit” illustrates the call frames established for the execution of a series of procedures. Procedure A establishes an ON-unit for the FIXEDOVERFLOW condition, and procedure B establishes an ON-unit for an UNDEFINEDFILE condition for the file PRINTFILE. When a FIXEDOVERFLOW condition is signaled in procedure C, PL/I locates the ON-unit established in procedure A and activates the corresponding ON-unit. When PL/I activates an ON-unit, it creates an activation record for the ON-unit and places the ON-unit on the call stack for execution, as if it were a unique block activation.

A: PROCEDURE OPTIONS(MAIN);
               ON FIXEDOVERFLOW BEGIN;
               ⋮
               END;
       CALL B;

B: PROCEDURE;
               ON UNDEFINEDFILE (PRINTFILE) OPEN
                       FILE (PRINTFILE) TITLE('SYS$OUTPUT');
               ⋮
       CALL C;

C: PROCEDURE;
       ⋮
       RETURN; /* Fixed overflow signaled */
       END;

Figure 9.3, “Execution of an ON-Unit” illustrates the preceding example.

Figure 9.3. Execution of an ON-Unit
Execution of an ON-Unit A B C ON-unit

9.5.1. Default Handling for Main Procedures

If PL/I entered a program at a procedure containing the MAIN option, PL/I searches for ON-units and performs default condition handling as follows:

  1. PL/I searches for specific ON-units in the following order:

    1. A VAXCONDITION ON-unit established for the specific condition value that is being signaled

    2. A PL/I ON-unit established for a PL/I condition name, if PL/I defines a name for the condition

    3. An ANYCONDITION ON-unit

    If one of these ON-units exists, it is executed and the search is ended. If the ON-unit completes execution by handling the condition, the program continues at the point at which the condition was signaled.

  2. If none of the above are found in any call frame, the default PL/I condition handler performs one of the following:

    • 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. If the ENDPAGE condition is for SYSPRINT, no action is performed.

    • 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, its execution is attempted. If it executes a nonlocal GOTO or 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 UNIX condition handler. This handler prints a message and terminates the program.

    • If the signal is any condition other than ENDPAGE, ERROR with a fatal severity, or UNDERFLOW, 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 resignaling results in control returning to the system, the default UNIX condition handler prints a message. If the error is a fatal error, the default handler terminates the program; if the error is nonfatal, the program continues.

9.5.2. Default Handling for Non-Main Procedures

If the call frame at which the program was entered did not specify the MAIN option, the default condition handling is:

  1. PL/I searches for specific ON-units in the following order:

    1. A VAXCONDITION ON-unit established for the specific condition value that is being signaled

    2. A PL/I ON-unit established for a PL/I condition name, if PL/I defines a name for the condition

    3. An ANYCONDITION ON-unit

    If one of these ON-units exists, it is executed and the search is ended. If the ON-unit completes execution by handling the condition, the program continues at the point at which the condition was signaled.

  2. If no ON-units are found in any call frame, the condition is signaled to the caller which repeats steps one and two. If the resignal results in the return of control to the system, the default UNIX condition handler issues a message. If the error was a fatal error, the default UNIX handler terminates the program. Otherwise, the program continues.

The next example and Figure 9.4, “Search for an ON-Unit” illustrate a search for an ON-unit that will handle a decimal overflow condition. The reverse numbers indicate the sequence in which the search takes place.

A: PROCEDURE OPTIONS(MAIN);
       ON ERROR BEGIN;  ❹ /* ERROR signaled */

               ⋮ ❼ /* Execute ON-unit */
       END;
       CALL B;  ❸
       ⋮
B: PROCEDURE; ❻ /* Look for ERROR */
       ⋮
       CALL C;
       ⋮
       RETURN;  ❷
       END;

C: PROCEDURE; ❺ /* Look for ERROR */
       ⋮
       RETURN; ❶
       END;

Referring to the example above and Figure 9.4, “Search for an ON-Unit”, you can see that PL/I takes the following actions:

❶

Searches the call frame (C) in which the condition was signaled for three specific ON-units: VAXCONDITION (SS$_DECOVF), FIXEDOVERFLOW, ANYCONDITION

❷

Searches the previous call frame (B)

❸

Reaches the main procedure (A) ON-units

❹

Signals the ERROR condition

❺

Searches the call frame (C) ERROR ON-unit

Searches the previous call frame (B)

❼

Locates the ERROR ON-unit in the main procedure (A)

Figure 9.4. Search for an ON-Unit
Search for an ON-Unit A B C

9.5.3. Multiple Conditions

If a second condition is signaled during the execution of an ON-unit, PL/I searches for an ON-unit that will handle the second condition, beginning in the call frame in which the second condition was signaled.

The next example and Figure 9.5, “Effect of Multiple Conditions” illustrate the search sequence followed when a second condition occurs during the execution of an ON-unit. The reverse numbers indicate the order of execution. The ERROR condition in procedure C is handled by the ON-unit established in procedure B. During the execution of this ON-unit, a FIXEDOVERFLOW condition is signaled. PL/I locates the ON-unit (shown in the Figure 9.5, “Effect of Multiple Conditions” as a box with dotted lines) established for FIXEDOVERFLOW conditions in procedure C and gives it control.

A: PROCEDURE OPTIONS(MAIN); ❶

       ⋮
       CALL B;

B: PROCEDURE;  ❷
       ON ERROR BEGIN;

       ⋮
       END;
       CALL C;

C: PROCEDURE;  ❸ /* Original ERROR signaled */  ❹
       ON FIXEDOVERFLOW BEGIN;

       ⋮
       END;

Figure 9.5, “Effect of Multiple Conditions” illustrates the preceding example.

Figure 9.5. Effect of Multiple Conditions
Effect of Multiple Conditions A B C

Note that if the second condition is the same condition as the first, and the ON-unit does not establish another ON-unit, the same ON-unit will be executed repeatedly as the condition is signaled. A similar situation results when a STOP statement is executed within a FINISH or ANYCONDITION ON-unit; the program will enter an infinite loop when the STOP statement executes. The STOP statement signals FINISH, the current ON-unit is re-executed, the STOP statement is executed again, and so on.

In a PL/I program, an ANYCONDITION ON-unit or a VAXCONDITION ON-unit established specifically to handle the SS$_UNWIND condition is invoked during the unwind. The following example illustrates a VAXCONDITION ON-unit:

DECLARE SS$_UNWIND GLOBALREF VALUE FIXED BINARY(31);
ON VAXCONDITION(SS$_UNWIND) BEGIN;
       CLOSE FILE(DATA_REC_TEMP) ENVIRONMENT(DELETE('0'B));
       END;

Although it is not specifically resignaled, when an ON-unit that is handling the unwind condition completes execution, the unwind continues.

Note that when an ANYCONDITION ON-unit executes a nonlocal GOTO statement, the nonlocal GOTO causes an unwind, and the first ON-unit that is given control is the ANYCONDITION ON-unit itself. Thus, an infinite loop occurs. To avoid this situation, an ANYCONDITION ON-unit can contain the following lines:

ON ANYCONDITION BEGIN;
DECLARE SS$_UNWIND GLOBALREF VALUE FIXED;
IF ONCODE() = SS$_UNWIND THEN GOTO OKAY;
⋮
OKAY: END;

This check for the condition SS$_UNWIND ensures that if a nonlocal GOTO is executed in this ON-unit, it will not cause the ON-unit to be re-executed.

9.6. Scope of ON-Units

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

  • Another ON statement is specified for the same condition in a descendent block. The ON-unit established within the descendent block remains in effect as long as the descendent 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.

  • 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 canceled.

9.7. ON-Unit Examples

The following examples illustrate some typical ON-units. The first example establishes an ON-unit for the FINISH condition. The ON-unit ensures that two files are closed properly, and calls a routine that stops a timer in an orderly fashion.

ON FINISH BEGIN;
       CLOSE FILE(INFILE);
       CLOSE FILE(OUTFILE);
       CALL TIMER_END;
       END;

Normally, the FINISH ON-unit should be declared in the main procedure; however, it will be executed on image exit if it is established in any block that is active when that occurs.

The next example contains an ERROR ON-unit that will terminate a program in an orderly fashion, should some error occur that is not handled by a specific ON-unit.

DECLARE STATUS FIXED BINARY(31);
⋮
ON ERROR BEGIN;
       CLOSE FILE (INFILE);
       CLOSE FILE (OUTFILE);
       STATUS = ONCODE();
       GOTO FINIS;
       END;
⋮
FINIS: RETURN (STATUS);

The ERROR ON-unit provides a cleanup procedure to ensure that the files identified as INFILE and OUTFILE are properly closed before the image exits. The ON-unit saves the value returned by ONCODE in the variable STATUS, and transfers control to a RETURN statement that returns the numeric value to the caller. If the procedure was invoked by a RUN command, this value is returned to the command interpreter, which in turn displays on the terminal the mnemonic code for the error and the error message.

The next example contains an ON-unit that changes the value of a bit variable when end-of-file is encountered.

DECLARE STATE_PTR POINTER,
       STATE_FILE FILE,
       EOF BIT(1) STATIC INIT('0'B);

ON ENDFILE(STATE_FILE) EOF = '1'B;
       OPEN FILE(STATE_FILE) INPUT SEQUENTIAL;
       READ FILE(STATE_FILE) SET(STATE_PTR);
       DO WHILE (^EOF);
               ⋮
               READ FILE(STATE_FILE) SET(STATE_PTR);
               END;

The procedure reads the records in the file STATE_FILE until it encounters end-of-file. At that point, the ON-unit executes and changes the value of EOF from 0 to 1. This action causes the test in the DO WHILE statement to fail, terminating the loop that reads the records.

The following example shows an ON-unit that consists of a sequence of statements in a begin block:

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.

The following example shows a null statement specified for an ON-unit:

ON ENDPAGE(SYSPRINT);

This ON-unit causes PL/I to continue output on a terminal regardless of the number of lines already output. The null statement indicates that no processing is to occur when the condition occurs. Program execution continues as if the condition had been handled.

See Chapter 4, File Organizations and Input/Output demonstrates an ON-unit that handles errors encountered during record I/O operations.

9.8. Condition-Handling Built-In Functions

The following sections discuss the two PL/I built-in functions that are useful for condition handling: ONCODE, and ONFILE.

9.8.1. ONCODE Built-In Function

All UNIX-defined conditions have symbolic names associated with them. When thePL/I ONCODE built-in function is invoked in an ON-unit, it returns the value of the associated global symbol. This 32-bit status value describes the condition that was signaled. The low-order three bits of the status value contain the severity of the condition (success, warning, error, or fatal). Severity is important when no ON-unit exists for a condition, and default condition handling is performed by PL/I (see Section 9.5, “Search Path for ON-Units”). This number is program dependent.

All symbolic names associated with UNIX condition values are defined as system global symbols. Thus, you can declare the names for these values in a PL/I program using the GLOBALREF attribute, and refer to them symbolically. The linker will automatically locate the definitions of the symbols in the default system libraries when the procedure is linked. You can use the system global symbol names to refer to the values returned by the ONCODE built-in function in an condition-handling ON-unit.

Table 9.2, “ONCODE Values for PL/I ON Conditions” lists the PL/I keyword condition names and the UNIX global symbol names associated with each condition name.

Table 9.2. ONCODE Values for PL/I ON Conditions
PL/I ConditionUNIX Global Symbol Name[a]
AREA[b]See the Endless PL/I for POSIX Systems Reference Manual for a discussion of allocation in areas.
CONDITIONPLI$_CONDITION
CONVERSIONPLI$_CONVERSION if a SIGNAL CONVERSION statement was executed, otherwise PLI$_ONCNVPOS (PLI$_ONCNVPOS could change in a future release of PL/I)
ENDFILEPLI$_ENDFILE
ENDPAGEPLI$_ENDPAGE
ERRORA specific status value associated with the error that caused the condition to be signaled[c]
FINISHPLI$_FINISH
FIXEDOVERFLOWSS$_DECOVF or SS$_INTOVF
KEYPLI$_KEY
OVERFLOWSS$_FLTOVF
STORAGEPLI$_STORAGE
STRINGRANGE[b]PLI$_STRRANGE
SUBSCRIPTRANGE ???PLI$_SUBRANGE
UNDEFINEDFILEPLI$_UNDEFINEDFILE
UNDERFLOWSS$_FLTUND
VAXCONDITIONAny user-defined condition or UNIX signal global symbol value that was signaled
ZERODIVIDESS$_FLTDIV or SS$_INTDIV

[a] If a PL/I condition is explicitly specified in a SIGNAL statement, the ONCODE value corresponds to the condition message associated with the condition, for example, PLI$_UNDFILE, PLI$_KEY, and so on.

[b] Only signalled via SIGNAL statement. No hardware support on UNIX.

[c] These names correspond to the identification fields in the run-time messages.


9.8.2. ONFILE Built-In Function

The ONFILE built-in function returns the name of the file constant for which the current file-related condition was signaled. Its format is given in the Endless PL/I for POSIX Systems Reference Manual.

The ONFILE built-in function can be used in the following ON-units:

  • An ON-unit established for the KEY, ENDFILE, ENDPAGE, and UNDEFINEDFILE conditions

  • A VAXCONDITION ON-unit established for I/O errors that can occur during file processing

  • An ERROR ON-unit that receives control as a result of the default PL/I action for file-related errors, which is to signal the ERROR condition

The returned value is a varying-length character string. If referenced outside an ON-unit or within an ON-unit that is executed as a result of a SIGNAL statement, the ONFILE function returns a null string.

9.8.3. ONKEY Built-In Function

The ONKEY built-in function returns the key value that caused the KEY condition to be signaled during an I/O operation to a file that is being accessed by key. Its format is given in the Endless PL/I for POSIX Systems Reference Manual.

This built-in function can be used in an ON-unit established for these conditions:

  • The KEY, ENDFILE, or UNDEFINEDFILE conditions

  • An ERROR ON-unit that receives control as a result of the default PL/I action for the KEY condition, which is to signal the ERROR condition

The returned key value is a varying-length character string. If referenced outside an ON-unit, or within an ON-unit executed as a result of the SIGNAL statement, the ONKEY built-in function returns a null string.