Document revision date: 30 March 2001

## OpenVMS User's Manual

Previous | Contents | Index

## 16.15 Handling File I/O Errors

Use the /ERROR qualifier with the OPEN, READ, or WRITE command to suppress system error messages and to pass control to a specified label. If an error occurs during an input or output operation, the /ERROR qualifier overrides all other error-control mechanisms (except the /END_OF_FILE qualifier on the READ command).

The following example uses the /ERROR qualifier with the OPEN command:

    $ OPEN/READ/ERROR=CHECK FILE CONTINGEN.DOC
       .
       .
       .
    $ CHECK:
    $  WRITE SYS$OUTPUT "Error opening file"

The OPEN command requests that the file CONTINGEN.DOC be opened for reading. If the file cannot be opened (for example, if the file does not exist), the OPEN command returns an error condition and transfers control to the CHECK label.

The error path specified by the /ERROR qualifier overrides the current ON condition established for the command level. If an error occurs and the target label is successfully given control, the reserved global symbol $STATUS retains the code for the error. You can use the F$MESSAGE lexical function in your error-handling routine to display the message in $STATUS.

In the following example, the lexical function F$MESSAGE is used to display the contents of the F$STATUS lexical:

    $ OPEN/READ/ERROR=CHECK FILE 'P1'
       .
       .
       .
    $ CHECK:
    $  ERR_MESSAGE = F$MESSAGE($STATUS)
    $  WRITE SYS$OUTPUT "Error opening file: ",P1
    $  WRITE SYS$OUTPUT ERR_MESSAGE
       .
       .
       .

### 16.15.1 Default Error Actions

If an error occurs while you are using the OPEN, READ, WRITE, or CLOSE command and you do not specify an error action, the current ON command action is taken.

When a READ command receives an end-of-file message, the error action is determined as follows:

- If you use the /END_OF_FILE qualifier, control is passed to the specified label.
- If you do not use the /END_OF_FILE qualifier, control is passed to the label specified with the /ERROR qualifier.
- If you do not specify either qualifier (/END_OF_FILE or /ERROR), the current ON command action is taken.

## 16.16 Techniques for Controlling Execution Flow

The normal flow of execution in a command procedure is sequential: the commands in the procedure execute in order until the end of the file is reached. However, you can also control whether certain statements are executed or the conditions under which the procedure should continue executing.

The following sections discuss:

- The DCL commands that you can use to control or alter the flow of execution:
  - IF, THEN, ELSE
  - GOTO
  - GOSUB
  - CALL
- Using command blocks
- Writing case statements
- Writing loops

### 16.16.1 Using the IF Command

The IF command tests the value of an expression and executes a command or block of commands when the result of the expression is true. When the result of the expression is false, one of the following occurs:

- When one command follows the THEN command, it is not executed and the following command executes.
- When a block of commands follows the THEN command and the ELSE command is not specified, the command immediately following the ENDIF command executes.
- When the ELSE command is specified, the command or block of commands following the ELSE command executes.

DCL provides two distinct formats for the IF command. The first format executes a single command when the expression specified to the IF command is true, as discussed in Chapter 15.

DCL also provides a block-structured IF format. The block-structured IF command executes more than one command if the expression specified is true and accepts an optional ELSE statement, which executes one or more commands if the expression is false.

### 16.16.2 Using the THEN Command

To execute more than one command when an expression is true, specify the THEN command as a verb (a DCL command preceded by a dollar sign) and terminate the resulting block-structured statement with an ENDIF statement.

In the following example, the THEN statement is used as a verb:

    $ IF expression
    $   THEN
    $       command
    $       command
       .
       .
       .
    $ ENDIF

### 16.16.3 Using the ELSE Command

To execute one or more commands when an expression is false, specify the ELSE statement as a verb and terminate the resulting block-structured statement with an ENDIF statement.

In the following example, the ELSE command is used as a verb:

    $ IF expression
    $   THEN
    $       command
    $       command
       .
       .
       .
    $   ELSE
    $       command
    $       command
       .
       .
       .
    $ ENDIF

### 16.16.4 Using Command Blocks

Command blocks can be executed in several ways, depending on whether you leave the commands in the same command procedure or put them in another command procedure and execute them there. The guidelines are as follows:

- If you leave the commands in the command procedure, place them after the THEN statement. For example:

      $ IF condition
      $ THEN  command
              command
         .
         .
         .
      $ ENDIF

- If you place the commands in a separate procedure, make the call to that command procedure as part of the THEN statement. For example:

      $ IF condition
      $ THEN @command_procedure
      $ ELSE command
      $      command
      $ ENDIF

- You can continue to specify the nonblock-structured IF format and direct flow to a labeled region when the condition specified is met. For example:

      $ IF not condition THEN GOTO END_LABEL
         .
         .
         .
      $END_LABEL:

You can execute a block of commands after the THEN command when the result of the IF expression is true. When you use a block of commands, place the THEN command as the first command on the line following the IF command.

In the following example, two SET TERMINAL commands execute and the procedure transfers control to the label PROCEED when F$MODE equals "INTERACTIVE". When F$MODE does not equal "INTERACTIVE", the procedure exits.

    $ IF F$MODE () .EQS. "INTERACTIVE"
    $ THEN
    $    SET TERMINAL/DEVICE=VT200
    $    SET TERMINAL/WIDTH=132
    $    GOTO PROCEED
    $ ENDIF
    $ EXIT
    $PROCEED:

The following example illustrates how to use a block of commands with the IF command in conjunction with the ELSE command:

    $ INQUIRE DEV "Device to check"
    $ IF F$GETDVI(DEV, "EXISTS")
    $ THEN
    $    WRITE SYS$OUTPUT "The device exists."
    $    SHOW DEVICE 'DEV'
    $    SET DEVICE/ERROR_LOGGING 'DEV'
    $ ELSE
    $    WRITE SYS$OUTPUT "The device does not exist."
    $    WRITE SYS$OUTPUT "Error logging has not been enabled."
    $ ENDIF
    $ EXIT

When the condition is true, the procedure writes a message to SYS$OUTPUT and executes the SHOW DEVICE and SET DEVICE commands. When the condition is not true, the procedure writes two messages to SYS$OUTPUT.

When you use the IF-THEN-ELSE language construct, observe the following restrictions:

- Include no more than 15 levels of nested IF statements.
- Terminate a command block started by a THEN statement with either an ELSE or an ENDIF statement.
- Terminate a command block started by an ELSE statement with an ENDIF statement.
- Include a THEN statement as the first executable statement following an IF statement.
- Do not specify a label on a line containing a THEN or an ELSE statement. You can, however, specify a label on a line containing an ENDIF statement. Programs can branch within the current command block but branching into the middle of another command block is not recommended.

True Expressions

The expression following the IF command can consist of one or more numeric constants, string literals, symbolic names, or lexical functions separated by logical, arithmetic, or string operators. An expression is true when it has one of the following values:

- An odd integer value
- A character string value that begins with any of the letters Y, y, T, or t
- A character string value that contains numbers that form an integer with an odd value (for example, the string "27")

False Expressions

An expression is false when it has one of the following values:

- An even integer value
- A character string value that begins with any letter other than Y, y, T, or t
- A character string value that contains numbers that form an integer with an even value (for example, the string "28")

Writing Expressions

When you write an expression for an IF command, adhere to the following rules:

- When you use symbols in IF statements, their values are automatically substituted. Do not use apostrophes (') as substitution operators unless you need to force iterative translation.
- String comparison operators end in the letter S. For example, use operators such as .EQS., .LTS., and .GTS. to compare strings. By contrast, the operators .EQ., .LT., and .GT. are used for comparing integers.
- When you test to see whether two strings are equal, the strings must use the same case in order for DCL to find a match. That is, the string "COPY" does not equal the string "copy" or the string "CoPy."

The following examples illustrate expressions that can be used with the IF command. For additional examples, refer to the description of the IF command in the OpenVMS DCL Dictionary.

The first example uses a logical operator and executes only one command following the THEN statement. When the symbol CONT is not true, the procedure exits.

    $ INQUIRE CONT "Do you want to continue [Y/N]"
    $ IF .NOT. CONT THEN EXIT
       .
       .
       .

The following example uses a symbol and a label within the IF expression:

    $ INQUIRE CHANGE "Do you want to change the record [Y/N]"
    $ IF CHANGE THEN GOTO GET_CHANGE
       .
       .
       .
    $ GET_CHANGE:
       .
       .
       .

When the symbol CHANGE is true, the procedure transfers control to the label GET_CHANGE. Otherwise, the procedure executes the command following the IF command.

The next example illustrates two different IF commands:

    $ COUNT = 0
    $ LOOP:
    $    COUNT = COUNT + 1
    $    IF COUNT .EQ. 9 THEN EXIT
    $    IF P'COUNT' .EQS. "" THEN EXIT
       .
       .
       .
    $ GOTO LOOP

The first IF command compares two integers; the second IF command compares two strings. Note that the .EQ. operator is used for the integer comparison and the .EQS. operator is used for the string comparison.

First, the value of COUNT is compared to the integer 9. When the values are equal, the procedure exits; when the values are not equal, the procedure continues. The loop terminates after eight parameters (the maximum number allowed) have been processed.

In the second IF expression, the string value of the symbol P'COUNT' is compared to a null string to see whether the symbol is undefined. Note that you must use apostrophes to force iterative substitution of the symbol COUNT. For example, when COUNT is 2, the result of the first translation is P2. Then, the value of P2 is used in the string comparison.

You can also execute a separate command procedure when the result of the IF expression is true. The following example executes the command procedure EXIT_ROUTINE.COM when the result of the IF expression is true:

    $ GET_COMMAND_LOOP:
    $    INQUIRE COMMAND -
         "Enter command (DELETE, DIRECTORY, EXIT, PRINT, PURGE, TYPE)"
    $    IF COMMAND .EQS. "EXIT" THEN @EXIT_ROUTINE

### 16.16.5 Using the GOTO Command

The GOTO command passes control to a labeled line in a command procedure. (For additional information on label usage, refer to Chapter 15.) The GOTO command is especially useful within a THEN clause to cause a procedure to branch forward or backward. For example, when you use parameters in a command procedure, you can test the parameters at the beginning of the procedure and branch to the appropriate label.

The target label of a GOTO or GOSUB command cannot be inside either a separate IF-THEN-ELSE construct or a separate subroutine.

In the following example, the IF command checks that P1 is not a null string:

    $ IF P1 .NES. "" THEN GOTO OKAY
    $ INQUIRE P1 "Enter file spec"
    $ OKAY:
    $ PRINT/COPIES=10 'P1'
       .
       .
       .

If P1 is a null string, the GOTO command is not executed and the INQUIRE command prompts for a parameter value. Otherwise, the GOTO command causes a branch around the INQUIRE command. In either case, the procedure executes the PRINT command following the line labeled OKAY.

In the following example the GOTO command returns an error message because its target (TEST_1) is within an IF-THEN construct:

    $ GOTO TEST_1
    $ EXIT
    $ IF 1.EQ.1
    $       THEN WRITE SYS$OUTPUT "What are we doing here?"
    $ TEST_1:
    $       WRITE SYS$OUTPUT "Got to the label"
    $ ENDIF
    $ EXIT

#### 16.16.5.1 Avoiding Reexecution

You can also use the GOTO command to avoid reexecuting parts of the job that have completed successfully. To do this, follow these steps:

Step | Action
--- | ---
1 | Begin each possible starting point in the procedure with a label.
2 | After the label, use the SET RESTART_VALUE = label-name command to set the restarting point to that label. If the batch job is interrupted after the SET RESTART_VALUE = label-name command executes, the system assigns the appropriate label name to the global symbol BATCH$RESTART when the batch job restarts.
3 | At the beginning of the procedure, test the value of the symbol $RESTART. If $RESTART is true, execute a GOTO statement using the symbol BATCH$RESTART as the transfer label.

$RESTART Global Symbol

$RESTART is a reserved global symbol that the system maintains for you. $RESTART is true if one of your batch jobs was restarted after it was interrupted. Otherwise, $RESTART is false. You cannot delete the reserved global symbol $RESTART.

If a command procedure has SET RESTART_VALUE commands in it but you want the job to reexecute in its entirety, enter the SET ENTRY/NOCHECKPOINT command to delete the global symbol BATCH$RESTART. If you restart a job that was interrupted, the job starts executing in the section where it was interrupted.

This command procedure shows how to use restart values in a batch job:

    $ ! Set default to the directory containing
    $ ! the file to be updated and sorted
    $ SET DEFAULT DISK1:[ACCOUNTS.DATA84]
    $
    $ ! Check for restarting
    $ IF $RESTART THEN GOTO 'BATCH$RESTART'
    $
    $ UPDATE_FILE:
    $ SET RESTART_VALUE = UPDATE_FILE
       .
       .
       .
    $ SORT_FILE:
    $ SET RESTART_VALUE = SORT_FILE
       .
       .
       .
    EXIT

To submit this command procedure as a batch job that can be restarted, use the /RESTART qualifier to the SUBMIT command when you submit the job. Because interrupted jobs begin executing in the section where they are interrupted, if this job is interrupted during the SORT_FILE routine, it starts executing at the label SORT_FILE when it is restarted.

Most of your process environment is not maintained when the system fails. The only symbols maintained across a system failure are $RESTART and BATCH$RESTART. Therefore, you should redefine any symbols or process logical names used in your command procedure after each SET RESTART_VALUE command or in a THEN block that executes if $RESTART is true.
If you define symbols and logical names in a THEN block, the command GOTO 'BATCH$RESTART' should be the last command in the THEN block.

### 16.16.6 Using the GOSUB and RETURN Commands

The GOSUB command transfers control to a labeled subroutine in a command procedure. If the label does not exist in the command procedure, the procedure cannot continue executing and is forced to exit. (For complete information on labels, refer to Chapter 15.) You can nest the GOSUB command up to 16 times per procedure level.

The GOSUB command is a local subroutine call; it does not create a new procedure level. Consequently, all labels and local symbols defined in the current command level are available to a subroutine invoked with GOSUB.

The RETURN command terminates a subroutine and returns control to the command following the GOSUB command. You can specify a value for $STATUS with the RETURN command that overrides the value that DCL assigns to $STATUS at the end of the subroutine. This value must be an integer between zero and four or an equivalent expression. If you specify a value for $STATUS, DCL interprets this value as a condition code. If you do not specify a value for $STATUS, the current value of $STATUS is saved.

The following example shows how you can use the GOSUB command to transfer control to subroutines:

    $!
    $! GOSUB.COM
    $!
    $ SHOW TIME
    $ GOSUB TEST1            (1)
    $ WRITE SYS$OUTPUT "GOSUB level 1 has completed successfully."
    $ SHOW TIME
    $ EXIT
    $!
    $! TEST1 GOSUB definition
    $!
    $ TEST1:
    $     WRITE SYS$OUTPUT "This is GOSUB level 1."
    $     GOSUB TEST2       (2)
    $     RETURN %X1        (3)
    $!
    $! TEST2 GOSUB definition
    $!
    $ TEST2:
    $     WRITE SYS$OUTPUT "This is GOSUB level 2."
    $     WAIT 00:00:02
    $     RETURN            (4)

As you examine the example, note the following:

1. The first GOSUB command transfers control to the subroutine labeled TEST1.
2. The procedure executes the commands in subroutine TEST1, branching to the subroutine labeled TEST2.
3. The RETURN command in subroutine TEST1 returns control to the main command procedure and passes a value of 1 to $STATUS, indicating successful completion.
4. The RETURN command in subroutine TEST2 returns control to subroutine TEST1. Note that this command executes before command 3.

## 16.17 Creating New Command Levels

There are two ways to create new command levels:

- Nest command procedures by using an execute procedure (@) command inside one command procedure to invoke another command procedure (as described in Chapter 15).
- Use the CALL command to call a subroutine that exists within the command procedure.

### 16.17.1 Using the CALL Command

The CALL command transfers control to a labeled subroutine in a command procedure and creates a new procedure level. The CALL command allows you to keep more than one related command procedure in a single file, making the procedures easier to manage. The subroutine label, which must be unique, can precede or follow the CALL command in the command procedure. Chapter 15 contains rules for entering subroutine labels.

In addition to the label, you can pass up to eight optional parameters to the subroutine. For complete information on parameters, refer to Section 16.2.

Following are rules for using the CALL command:

- Sends output to SYS$OUTPUT
- Has an optional /OUTPUT qualifier that allows you to direct output from the subroutine to a file
- Uses a default file type .LIS for the output file
- Does not accept wildcard characters in the output file specification.

#### 16.17.1.1 CALL Command Defaults

Following are additional defaults associated with using the CALL command:

- You can nest subroutines called with the CALL command and procedures called with the execute procedure (@) command to a maximum of 32 command levels.
- Unless they are masked using the SET SYMBOL command, local symbols defined in an outer level are available to any inner procedure or subroutine level. Global symbols are available at any command level.
- Labels are valid only for the level in which they are defined.

#### 16.17.1.2 Beginning and Ending Subroutines

The SUBROUTINE and ENDSUBROUTINE commands define the beginning and the end of a CALL subroutine. The label defining the entry point to the subroutine immediately precedes the SUBROUTINE command. You can place the EXIT command immediately before the ENDSUBROUTINE command but it is not required to terminate the subroutine. The ENDSUBROUTINE command terminates the subroutine and transfers control to the command line immediately following the CALL command.

Command lines in a subroutine execute only when the subroutine is called with the CALL command. During the line-by-line execution of the command procedure, the command language interpreter skips all commands between the SUBROUTINE and the ENDSUBROUTINE commands.

The following restrictions apply to defining the scope of subroutine entry points and the use of label references:

- Subroutine entry points that are defined within another subroutine are local to that subroutine. You cannot call a subroutine if the subroutine entry point is within a separate subroutine block.
- If a subroutine entry point is located within an IF-THEN-ELSE block, you cannot call this subroutine from outside the IF-THEN-ELSE block.
- Every SUBROUTINE command must have a matching ENDSUBROUTINE command to end the subroutine.

In the following example, the call is not valid because the CALL BAR command is located outside of the MAIN subroutine:

    $ CALL BAR
    $
    $ MAIN: SUBROUTINE
    $
    $     BAR: SUBROUTINE
    $     ENDSUBROUTINE
    $
    $ ENDSUBROUTINE

For this CALL command to work, it must be placed within the SUBROUTINE and ENDSUBROUTINE points.

The call shown in this example in not allowed because it is within an IF-THEN-ELSE block:

    $ IF 1
    $ THEN
    $    BOB:SUBROUTINE
    $    ENDSUBROUTINE
    $ ENDIF
    $ CALL BOB

The following example includes two subroutines called SUB1 and SUB2. The subroutines do not execute until they are called with the CALL command.

    $
    $! CALL.COM
    $
    $! Define subroutine SUB1.
    $!
    $ SUB1: SUBROUTINE
            .
            .
            .
    $       CALL SUB2 !Invoke SUB2 from within SUB1.
            .
            .
            .
    $       @FILE  !Invoke another command procedure file.
            .
            .
            .
    $       EXIT
    $ ENDSUBROUTINE !End of SUB1 definition.
    $!
    $! Define subroutine SUB2.
    $!
    $ SUB2: SUBROUTINE
    $       EXIT
    $ ENDSUBROUTINE !End of SUB2 definition.
    $!
    $! Start of main routine. At this point, both SUB1 and SUB2
    $! have been defined but none of the previous commands have
    $! been executed.
    $!
    $ START:
    $       CALL/OUTPUT=NAMES.LOG  SUB1 "THIS IS P1"
            .
            .
            .
    $       CALL SUB2 "THIS IS P1" "THIS IS P2"
            .
            .
            .
    $ EXIT  !Exit this command procedure file.

The CALL command invokes the subroutine SUB1 and directs output to the file NAMES.LOG. Subroutine SUB1 calls subroutine SUB2. The procedure executes SUB2, invoking the command procedure FILE.COM with the execute procedure (@) command. When all the commands in SUB1 have executed, the CALL command in the main procedure calls SUB2 a second time. The procedure exits when SUB2 finishes executing.

Previous | Next | Contents | Index

| privacy and legal statement |
6489PRO_044.HTML