Compaq BASIC for OpenVMS
Alpha and VAX Systems
User Manual


Previous Contents Index

16.2.3 Exiting from Handlers

After processing an error, a handler typically clears the error so that program execution can continue. BASIC provides the following statements that clear the error condition and exit from the handler:

RETRY
CONTINUE
END HANDLER
END WHEN

These statements differ from each other in that they revert control of program execution to different points in the program. Examples of these statements are included in the following sections.

An additional statement, EXIT HANDLER, is provided to allow you to exit from a handler with the error still pending.

The END HANDLER statement identifies the end of the block of statements in the handler. The END WHEN statement marks the end of the protected region when a detached handler is used; it marks the end of the handler when an attached handler is used. If the handler does not process an error with an EXIT HANDLER, RETRY, or CONTINUE statement, the error is cleared by the END HANDLER or END WHEN statement; however, processing continues with the statement immediately after the protected region (and the attached handler, if one exists) where the error occurred. These statements do not return control to the protected region. This is known as "falling out the bottom of a handler." Be careful not to fall out of the bottom of a handler unintentionally.

Note that you cannot exit from a handler with the following statements:

Also, you cannot exit from a handler with a RESUME statement. The RESUME statement is valid only in blocks of code referred to by ON ERROR statements. Section 16.3 describes the ON ERROR statements.

16.2.3.1 RETRY Statement

You use the RETRY statement to clear the error and to execute the statement again that caused the error again. Be sure to take corrective action before trying the protected statement again. For example:


DECLARE REAL radius 
 
WHEN ERROR USE fix_it 
     INPUT "Please supply the radius of the circle"; radius 
END WHEN 
HANDLER fix_it 
     !trap overflow error 
     IF ERR = 48 
     PRINT "Please supply a smaller radius" 
     RETRY 
END HANDLER 
PRINT "The circumference of the circle is "; 2*PI*radius 

In FOR...NEXT loops, if the error occurs while BASIC is evaluating the limit or increment values, RETRY reexecutes the FOR statement; if the error occurs while BASIC is evaluating the index variable, RETRY reexecutes the NEXT statement. In UNTIL...NEXT and WHILE...NEXT loops, if the error occurs while BASIC is evaluating the relational expression, RETRY reexecutes the NEXT statement.

16.2.3.2 CONTINUE Statement

You can use the CONTINUE statement to clear the error and cause execution to continue at the statement immediately following the propagated error.

When the CONTINUE statement is within an attached handler, you can specify a target. The target can be a line number or label within the bounds of the associated protected region, in a surrounding protected region, or within an unprotected region; however, you must specify a target within the current program module. You cannot specify a target for the CONTINUE statement when it is in a detached handler. For example:


DIM LONG her_attributes(10),his_attributes(10) 
DECLARE INTEGER counter 
WHEN ERROR USE fix_it 
  DATA 12,2,35,21,25.5,32,32,30,15,4 
  FOR counter = 0 TO 12 
      READ her_attributes(counter) 
  NEXT counter 
  MAT his_attributes = her_attributes 
END WHEN 
   .
   .
   .
HANDLER fix_it 
    !Trap out of data 
    IF ERR = 57 
       THEN RESTORE 
            CONTINUE 
    ELSE EXIT HANDLER 
    END IF 
END HANDLER 

When a DEF function is invoked from a protected region and an error occurs that has not been handled, a CONTINUE statement with no target causes execution to resume at the statement following the one that invoked the function.

Note that if an error occurs in a loop control statement or SELECT or CASE statement, the CONTINUE statement causes BASIC to resume execution at the statement following the end of the loop structure (the NEXT, END CASE, or END SELECT statements).

Note

When you use the RETRY or the CONTINUE statement without a target, the compiler builds read only tables in the generated object file with information about statements in the associated protected regions. Therefore, when space is extremely critical, do not protect large regions with handlers containing RETRY or CONTINUE without a specified target.

16.2.3.3 EXIT HANDLER Statement

Unlike RETRY and CONTINUE, the EXIT HANDLER statement does not clear the error; rather, it allows you to exit from the handler with the error pending. This allows you to pass an error to the handler associated with the next outer protected region, or back to BASIC default error handling, or to the calling procedure.

When an error occurs within a nested protected region, control passes to the handler associated with the innermost protected region in which the error occurred. If the innermost handler does not handle the error, the error is passed to the next outer handler with the EXIT HANDLER statement. All handlers for any outer WHEN ERROR blocks are processed before reverting to default error handling or resignaling the calling procedure.

The following example shows two nested protected regions. Neither handler traps division by zero. If division by zero occurs, the handler associated with the innermost protected region, inner_handler, does not clear the error; therefore, the error is passed to the handler associated with the next outer protected region. Outer_handler does not clear this error either, and so the error is passed to the default error handler. This error is fatal and the program ends abnormally. Output is specific to VAX BASIC.


PROGRAM nesting 
 OPTION TYPE = EXPLICIT 
 DECLARE LONG divisor 
 DECLARE REAL dividend, quotient 
 WHEN ERROR USE outer_handler 
      INPUT "Enter divisor";Divisor 
      INPUT "Enter dividend";Dividend 
 
      WHEN ERROR USE inner_handler 
           Quotient = Dividend/Divisor 
           PRINT "The quotient is ";Quotient 
      END WHEN 
 
 END WHEN 
 HANDLER outer_handler 
         !Trap data format error 
         IF ERR = 50 
            THEN 
            PRINT "Illegal input...try again" 
            RETRY 
            ELSE PRINT "In outer_handler" 
                 PRINT "Reverting to default handling now" 
                 EXIT HANDLER 
         END IF 
 END HANDLER 
 HANDLER inner_handler 
         !Trap overflow/decimal error 
         IF ERR = 181 
            THEN CONTINUE 
            ELSE PRINT "Inside inner_handler" 
                 PRINT "Reverting to outer handler now" 
                 EXIT HANDLER 
         END IF 
 END HANDLER 
END PROGRAM 

VAX BASIC Output


Enter divisor? 0 
Enter dividend? 53 
Inside inner_handler 
Reverting to outer handler now 
Inside outer_handler 
Reverting to default handling now 
%BAS-F-DIVBY_ZER, Division by 0 
-BAS-USEPC_PSL, at user PC=001C18B3, PSL=03C000A4 
-SYSTEM-F-FLTDIV_F, arithmetic fault, floating divide by zero at 
PC=001C18B3, PSL=03C000A4 
-BAS-I-FROLINMOD, in module ERROR_7 

For more information about exiting program units while an error is pending, see Section 16.2.6.

16.2.4 Selecting the Severity of Errors to Handle

The OPTION HANDLE statement lets you specify the severity level of errors that are to be handled by an error handler in addition to the BASIC errors that can normally be handled or trapped. You can specify any one of the following error severity levels: BASIC, SEVERE, ERROR, WARNING, or INFORMATIONAL.

OPTION HANDLE = BASIC is the default, which is in effect if you do not specify an alternative in the OPTION HANDLE statement. Only BASIC errors that can be trapped transfer control to the current error handler when this option is in effect. Refer to Appendix B to determine which BASIC errors cannot be trapped.

When you specify an error severity level other than BASIC in the OPTION HANDLE statement, the following errors will transfer control to the error handler:

For example, if you specify OPTION HANDLE = ERROR, you can handle all BASIC and non BASIC errors of ERROR severity (both those that can and those that cannot be trapped), and all WARNING and INFORMATIONAL errors, but no SEVERE errors.

16.2.5 Identifying Errors

BASIC provides several built-in functions that return information about an error. You can use these functions inside your error handlers to determine details about the error and conditionally handle these errors. These functions include:

ERR
ERL
ERN$
ERT$
VMSSTATUS
RMSSTATUS

Note that if an error occurs in your program that is not a BASIC error or does not map onto a BASIC error, it is signaled as NOTBASIC ("Not a BASIC error" (ERR=194). In this case, you can use the built-in function VMSSTATUS to determine what caused the error.

16.2.5.1 Determining the Error Number (ERR)

You use the ERR function to return the number of the last error that occurred. Appendix B lists the number of each BASIC run-time error---for example, ERR 153 is "RECALREXI, Record already exists."


OPTION HANDLE = ERROR 
WHEN ERROR USE find_error 
   .
   .
   .
END WHEN 
 
HANDLER find_error 
  SELECT ERR 
        !Record already exists 
        CASE = 153 
                PRINT "Choose new record" 
                CONTINUE 
        CASE ELSE 
                EXIT HANDLER 
  END SELECT 
 
END HANDLER 

The results of ERR remain undefined until an error occurs. Although ERR remains defined as the number of the last error after control leaves the error handler, it is poor programming practice to refer to this variable outside the scope of an error handler.

16.2.5.2 Determining the Error Line Number (ERL)

After your program generates an error, the ERL function returns the BASIC line number of the signaled error. This function is valid only in line-numbered programs. The ERL function, like ERR, lets you set up branching to one of several paths in the code.

In the following example, the handler continues execution at different points in the program, depending on the value of ERL:


10 DECLARE INTEGER CONSTANT TRUE = -1 
20 WHEN ERROR USE err_handler 
   .
   .
   .
900 END WHEN 
1000 HANDLER err_handler 
       SELECT TRUE 
           CASE (ERR = 11) AND (ERL = 790) 
               !Is error end of file at line 790? 
               PRINT "Completed" 
               CONTINUE 
           CASE (ERR = 149) AND (ERL = 80) 
               !Is error not at end of file on line 80? 
               PRINT "CHECK ACCESS MODE" 
               CONTINUE 
           CASE ELSE 
               !Let BASIC handle any other errors 
               EXIT HANDLER 
1500   END SELECT 
2000 END HANDLER 
32000   CLOSE #5 
32767   END 

The results of ERL are undefined until an error occurs, or if the error occurs in a subprogram not written in BASIC. Although ERL remains defined as the line number of the last error even after control leaves the error handler, it is poor programming practice to refer to this variable outside the scope of an error handler.

If you reference ERL in a compilation unit with line numbers, code and data are included in your program to allow BASIC to determine ERL when an exception occurs. If you do not need to reference ERL, you can save program size and reduce execution time by compiling your program with the /NOLINE qualifier. Alpha BASIC uses the /NOLINE qualifier by default to compile programs. Even if you do not use any line numbers, you can reduce execution time by compiling with the /NOLINE qualifier.

If an error occurs in a subprogram containing line numbers, BASIC sets the ERL variable to the subprogram line number where the error was detected. If the subprogram also executes an EXIT HANDLER statement, control passes back to the outer procedure's handler. The error is assumed to occur on the statement where the call or invocation occurs.

16.2.5.3 Determining Where the Error Occurred (ERN$)

You use the ERN$ function to return the name of the program unit in which the error was detected. ERN$ returns the name of a main program, SUB, FUNCTION, or PICTURE subprogram, or DEF function. If the PROGRAM statement is used with a user-supplied identifier, the ERN$ value is the specified identifier for the main program. The results of ERN$ are undefined until the program generates an error.

In the following example, control passes to the main program for error handling if the error occurs in the module SUBARC:


HANDLER locat_ern 
  IF ERN$ = "SUBARC" 
     THEN PRINT "ERROR IS ";ERR 
          PRINT "RETURNING TO MAIN PROGRAM FOR ERROR HANDLING" 
          EXIT HANDLER 
     ELSE  PRINT "PROGRAM MODULE GENERATING ERROR IS ";ERN$ 
  END IF 
END HANDLER 

Note that ERN$ is invalid when an error occurs in a subprogram compiled with the /NOSETUP qualifier.

16.2.5.4 Determining the Error Message Text (ERT$)

You use the ERT$ function to access the message text associated with a specified error number. Use of the ERT$ function is not limited to the scope of the error handler; you can access ERT$ at any time. The following detached handler tests whether the error occurred in a DEF module named TSLFE, and, if so, prints the text of the signaled error and resumes execution:


HANDLER catch_it 
  IF ERN$ = "TSLFE" 
        THEN PRINT ERT$(ERR) 
        CONTINUE 
        ELSE EXIT HANDLER 
  END IF 
END HANDLER 

16.2.5.5 Determining OpenVMS Error Information

BASIC provides a built-in function, VMSSTATUS, that returns the originally signaled error before it is translated to a BASIC error. For example, for the BASIC error "End of file on device" (ERR=11), the VMSSTATUS function returns "RMS$_EOF" (RMS end of file). This function is useful when the error is NOTBASIC (ERR=194).

When there is no error pending, VMSSTATUS is undefined. The value returned by this function is the actual signaled error value. If non BASIC errors are being handled, the VMSSTATUS function might be the only way to find out which error caused the exception.

The following example shows a program that performs file I/O. The first WHEN ERROR block traps any errors that occur while the program is opening the file or requesting user input. The detached handler for this block checks the value of VMSSTATUS to determine the exception that occurred. The inner error handler handles two special errors, BAS$K_RECNOTFOU and BAS$K_RECBUCLOC, separately. If the error signaled does not correspond to one of these, the inner error handler passes control to the outer handler with the EXIT HANDLER statement. The outer handler sets the program status to VMSSTATUS. When the program exits, the operating system displays any status that is of warning severity or greater.


PROGRAM Tester 
 
 OPTION HANDLE = ERROR 
 EXTERNAL LONG CONSTANT BAS$K_RECNOTFOU, BAS$K_RECBUCLOC 
 DECLARE LONG Final_status 
 MAP (Rec_buffer)                           & 
      STRING Rec_key        = 5,            & 
      STRING Rest_of_record = 20 
 Final_status = 1 
 WHEN ERROR USE Global_handler 
      OPEN "My_database" FOR INPUT AS FILE #1  & 
           ,INDEXED FIXED                      & 
           ,ACCESS READ                        & 
           ,MAP Rec_buffer                     & 
           ,PRIMARY Rec_key 
 Get_key: 
     INPUT "Record to retrieve"; Rec_key 
     WHEN ERROR IN 
          GET #1%, KEY #0 EQ Rec_key 
          PRINT Rest_of_record 
     USE 
          SELECT ERR 
              CASE = BAS$K_RECNOTFOU 
                     PRINT "Record not found" 
                     CONTINUE Get_key 
              CASE = BAS$K_RECBUCLOC 
                     SLEEP 2% 
                     RETRY 
              CASE ELSE 
                   EXIT HANDLER 
          END SELECT 
     END WHEN 
    END WHEN 
    HANDLER Global_handler 
            Final_status = VMSSTATUS 
    END HANDLER 
 
END PROGRAM Final_status 

16.2.5.6 Determining RMS Error Information

The RMSSTATUS function lets you determine which RMS error caused a resulting BASIC error. You must specify an open channel as the first parameter to RMSSTATUS. If this channel is not open, the error "I/O channel not open" (ERR=9) is signaled. The second parameter to the function lets you specify either STATUS or VALUE; this parameter is optional. If you do not specify the second parameter, RMSSTATUS returns the STATUS value by default. STATUS represents the RMS "STS" field and VALUE corresponds to the RMS "STV" field.

The following example shows an error handler that prints both the status and the value of any RMS error:


WHEN ERROR IN 
     OPEN "file.txt" FOR OUTPUT AS FILE 1% 
     PRINT #1%, TIME$(0%) 
USE 
     !Error 12 is fatal system I/O failure 
     IF ERR = 12 
        THEN 
        PRINT "An unexpected RMS error has occurred:" 
        PRINT "Status = "; RMSSTATUS(1%) 
        PRINT "Value = "; RMSSTATUS(1%, VALUE) 
        EXIT HANDLER 
     END IF 
END WHEN 
 
CLOSE #1% 
GOTO done 
 
done: 
    END 

If you want to find an RMS status without knowing which particular channel to check, you can use VMSSTATUS to get the STATUS value (STS) if an error has occurred.

16.2.6 Ctrl/C Trapping

Error handling procedures are commonly used to trap user Ctrl/C responses. With Ctrl/C trapping enabled, control is transferred to an error handler if a user presses Ctrl/C during program execution. You enable Ctrl/C trapping in your program by invoking the built-in CTRLC function. For example:


Y% = CTRLC 

After you invoke the CTRLC function, a Ctrl/C entered at the terminal transfers control to the error handler. Once the Ctrl/C is trapped, you can include routines to interact with the program, as shown in the following example:


WHEN ERROR IN 
  Y% = CTRLC 
  OPEN 'FIL_DAT' FOR INPUT AS FILE #1% 
  INPUT "HOW MANY RECORDS"; Rec_read% 
  FOR I% = 1% TO Rec_read% 
    GET #1% 
    PRINT Name$, Address$, Emp_code% 
    PRINT 
  NEXT I% 
 
USE 
  !Trap ^C 
  IF (ERR = 28%) 
     THEN PRINT "CURRENT RECORD IS "; I% 
     ELSE EXIT HANDLER 
  END IF 
  CONTINUE Clean_up 
END WHEN 
   .
   .
   .
Clean_up: 
  CLOSE #1% 
  PRINT "END OF PROCESSING" 
END 

Output


SMITH, DEXTER 231 COLUMBUS ST             09341 
 
TRAVIS, JOHN  PO BOX 80                   64119 
 
^C 
 
THE CURRENT RECORD IS 3 
 
END PROCESSING 

Note that the error condition is still pending until the error handler executes the CONTINUE statement. Therefore, if you press Ctrl/C a second time while the error handler is executing, control returns to the BASIC error handler, which terminates the program.

To disable Ctrl/C trapping, use the RCTRLC function. The RCTRLC function disables only Ctrl/C trapping, not the Ctrl/C interrupts themselves.

16.2.7 Handling Errors in Multiple-Unit Programs

You can use WHEN ERROR constructs anywhere in your main program or program modules. Procedure and function invocations, such as invocations of DEF and DEF* functions and SUB, FUNCTION, and PICTURE subroutines, as well as non BASIC programs, are valid within protected regions. GOTO and GOSUB statements are valid within handlers provided that the target is within the handler, an outer handler, or an unprotected region. Note, however, that a detached handler cannot appear within DEF or DEF* functions without the associated protected region.

When an error occurs within nested protected regions, BASIC maintains the same priorities for handler use; control always passes to the handler associated with the innermost protected region in which the error occurred. When an exception occurs, all handlers for any outer WHEN ERROR blocks are processed before the program reverts to default error handling. Outer handlers are invoked when an inner handler executes an EXIT HANDLER statement. When there are no more outer handlers, and the outermost handler executes an EXIT HANDLER statement, program control reverts to the handler associated with the calling routine. For example:


SUB LIST(A$) 
  WHEN ERROR USE sub_handler 
 
      OPEN A$ FOR INPUT AS FILE #12% 
  Get_data: 
      LINPUT #12%, B$ 
      PRINT B$ 
      GOTO Get_data 
  END WHEN 
  HANDLER sub_handler 
    !Trap end of file 
    IF ERR <> 11% 
        THEN EXIT HANDLER 
    END IF 
END HANDLER 
CLose_up: 
   CLOSE #12% 
END SUB 

You can call a subprogram while an error is pending; however, if you do, the subprogram cannot resignal an error back to the calling program. If the subprogram tries to resignal an error, BASIC signals "Improper error handling" and program execution terminates.

The following rules apply to error handling in function definitions:

For example:


     WHEN ERROR IN 
     .
     .
     .
     Invoke_def: 
       A% = FNIN_PUT%("PROMPT") 
 
     USE 
        PRINT "ERROR"; ERT$(ERR%); 
        IF ERN$ = "FNIN_PUT" 
           THEN PRINT "IN FUNCTION" 
                CONTINUE 
           ELSE PRINT "IN MAIN" 
                CONTINUE Invoke_def 
        END IF 
     END WHEN 
 
     Main_code: 
     DEF FNIN_PUT%(P$) 
      WHEN ERROR IN 
        PRINT P$ 
        INPUT LINE_IN$ 
        FNIN_PUT% = INTEGER(LINE_IN$) 
     USE 
         IF ERR = 50 
            THEN RETRY 
            ELSE EXIT HANDLER 
         END IF 
     END WHEN 
     END DEF 

Note

If you invoke a GOSUB statement or a DEF* function from within a protected region and the invoked procedure is outside of any protected region, all pending errors are handled by the WHEN ERROR handler unless a previously executed ON ERROR statement specifies otherwise.


Previous Next Contents Index