Compaq Fortran
User Manual for
OpenVMS Alpha Systems


Previous Contents Index


Chapter 11
Using OpenVMS Record Management Services

This chapter describes how to call OpenVMS Record Management Services (RMS) directly from Compaq Fortran programs. RMS is used by all utilities and languages for their I/O processing, allowing files to be accessed efficiently, flexibly, with device independence, and taking full advantage of the capabilities of the OpenVMS operating system.

You need to know the basic concepts concerning files on OpenVMS systems and system-service calling conventions before reading this chapter. Basic file concepts are covered in the Guide to OpenVMS File Applications, and system-service calling conventions are covered in Chapter 10.

You should also have access to the OpenVMS Record Management Services Reference Manual. Although not written specifically for Compaq Fortran programmers, that manual is the definitive reference source for all information on the use of RMS.

This chapter will help you understand the material in the OpenVMS Record Management Services Reference Manual in terms of Fortran concepts and usage. You should also be able to more fully understand the material in the Guide to OpenVMS File Applications, which covers more areas of RMS in greater detail than this chapter.

The easiest way to call RMS services directly from Compaq Fortran is to use a USEROPEN routine, which is a subprogram that you specify in an OPEN statement. The Compaq Fortran Run-Time Library (RTL) I/O support routines call the USEROPEN routine in place of the RMS services at the time a file is first opened for I/O.

The Compaq Fortran RTL sets up the RMS data structures on your behalf with initial field values that are based on parameters specified in your OPEN statement. This initialization usually eliminates most of the code needed to set up the proper input to RMS Services. If you specify the USEROPEN keyword in the OPEN statement, control transfers to the specified USEROPEN routine that can further change RMS data structures and then call the appropriate RMS services, including SYS$OPEN (or SYS$CREATE) and SYS$CONNECT.

When you use USEROPEN routines, you can take advantage of the power of RMS without most of the declarations and initialization code normally required. Section 11.3 describes how to use USEROPEN routines and gives examples. You should be familiar with the material in Section 11.1 before reading Section 11.3.

11.1 RMS Data Structures

RMS system services have so many options and capabilities that it is impractical to use anything other than several large data structures to provide their arguments. You should become familiar with all of the RMS data structures before using RMS system services.

The RMS data structures are as follows:

The RMS data structures are used both to pass arguments to RMS services and to return information from RMS services to your program. In particular, an auxiliary structure, such as a NAM or XAB block, is commonly used explicitly to obtain information optionally returned from RMS services.

The OpenVMS Record Management Services Reference Manual describes how each of these data structures is used in calls to RMS services. In this section, a brief overview of each block is given, describing its purpose and how it is manipulated in Compaq Fortran programs.

In general, there are six steps to using the RMS control blocks in calls to RMS system services:

  1. Declare the structure of the blocks and the symbolic parameters used in them by including the appropriate definition library modules from the Fortran default library FORSYSDEF.TLB.
  2. Declare the memory allocation for the blocks that you need with a RECORD statement.
  3. Declare the system service names by including the library module $SYSSRVNAM from FORSYSDEF.TLB.
  4. Initialize the values of fields needed by the service you are calling. The structure definitions provided for these blocks in the FORSYSDEF library modules provide only the field names and offsets needed to reference the RMS data structures. You must assign all of the field values explicitly in your Compaq Fortran program.
    Two fields of each control block are mandatory; they must be filled in with the correct values before they are used in any service call. These are the block id (BID, or COD in the case of XABs) and the block length (BLN). These are checked by all RMS services to ensure that their input blocks have proper form.
    These fields must be assigned explicitly in your Compaq Fortran programs, unless you are using the control blocks provided by the Fortran RTL I/O routines, which initialize all control block fields. See Table 11-1 for a list of the control field values provided by the Fortran RTL I/O routines.
  5. Invoke the system service as a function reference, giving the control blocks as arguments according to the specifications in the RMS reference manual.
  6. Check the return status to ensure that the service has completed successfully.

Steps 1-4 are described for each type of control block in Section 11.1.2 to Section 11.1.5. See Section 11.2 for descriptions of steps 5 and 6.

11.1.1 Using FORSYSDEF Library Modules to Manipulate RMS Data Structures

The definition library FORSYSDEF.TLB contains the required Fortran declarations for all of the field offsets and symbolic values of field contents described in the OpenVMS Record Management Services Reference Manual. The appropriate INCLUDE statement needed to access these declarations for each structure is described wherever appropriate in the text that follows.

In general, you need to supply one or more RECORD statements to allocate the memory for the structures that you need. See the OpenVMS Record Management Services Reference Manual for a description of the naming conventions used in RMS service calls. Only the convention for the PARAMETER declarations is described here.

The FORSYSDEF library modules contain several different kinds of PARAMETER declarations. The declarations are distinguished from each other by the letter following the dollar sign ($) in their symbolic names. Each is useful in manipulating field values, but the intended use of the different kinds of PARAMETER declarations is as follows:

For most of the FAB, RAB, NAM, and XAB fields that are not supplied with symbolic values, you will need to supply sizes or pointers. For sizes, you can use ordinary numeric constants or other numeric scalar quantities. To set the maximum record number into the FAB$L_MRN field, you could use the following statement:


  MYFAB.FAB$L_MRN = 5000 

To supply the required pointers, usually from one block to another, you must use the %LOC built-in function to retrieve addresses. To fill in the FAB$L_NAM field in a FAB block with the address of the NAM block that you want to use, you can use the following program fragment:


  INCLUDE '($FABDEF)' 
  INCLUDE '($NAMDEF)' 
  ...
  RECORD /FABDEF/ MYFAB, /NAMDEF/ MYNAM 
  ...
  MYFAB.FAB$L_NAM = %LOC(MYNAM) 

11.1.2 The File Access Block

The File Access Block (FAB) is used for calling the following services:
SYS$CLOSE
SYS$CREATE
SYS$DISPLAY
SYS$ENTER
SYS$ERASE
SYS$EXTEND
SYS$OPEN
SYS$PARSE
SYS$REMOVE
SYS$RENAME
SYS$SEARCH

The purpose of the FAB is to describe the file being manipulated by these services. In addition to the fields that describe the file directly, there are pointers in the FAB structure to auxiliary blocks used for more detailed information about the file. These auxiliary blocks are the NAM block and one or more of the XAB blocks.

To declare the structure and parameter values for using FAB blocks, include the $FABDEF library module from FORSYSDEF. For example:


  INCLUDE '($FABDEF)' 

To examine the fields and values declared, use the /LIST qualifier after the right parenthesis. Each field in the FAB is described at length in the OpenVMS Record Management Services Reference Manual.

If you are using a USEROPEN procedure, the actual allocation of the FAB is performed by the Fortran Run-Time Library I/O support routines, and you only need to declare the first argument to your USEROPEN routine to be a record with the FAB structure. For example:

Calling program:


  ...
  EXTERNAL MYOPEN 
  ...
  OPEN (UNIT=8, ..., USEROPEN=MYOPEN) 
  ...

USEROPEN routine:


  INTEGER FUNCTION MYOPEN(FABARG, RABARG, LUNARG) 
  INCLUDE '($FABDEF)' 
  ...
  RECORD /FABDEF/ FABARG 
  ...

Usually, you need to declare only one FAB block, but some situations you need to use two. For example, the SYS$RENAME service requires one FAB block to describe the old file name and another to describe the new file name. In any of these cases, you can declare whatever FAB blocks you need with a RECORD statement. For example:


  INCLUDE '($FABDEF)' 
  ...
  RECORD /FABDEF/ OLDFAB, NEWFAB 

If you use any of the above service calls without using a USEROPEN routine, you must initialize the required FAB fields in your program. The FAB fields required for each RMS service are listed in the descriptions of the individual services in the OpenVMS Record Management Services Reference Manual. Most services also fill in output values in the FAB or one of its associated blocks. Descriptions of these output fields are also provided with the service descriptions.

In the example programs in the OpenVMS Record Management Services Reference Manual, these initial field values are described as they would be used in MACRO programs, where the declaration macros allow initialization arguments. In each case where the MACRO example shows a field being initialized in a macro, you must have a corresponding initialization at run time in your program.

The OpenVMS Record Management Services Reference Manual contains an example that shows the use of the ALQ parameter for specifying the initial allocation size of the file in blocks:


  !    Program that uses XABDAT and XABDAT_STORE 
   . 
   . 
   . 
 
  MYFAB: $FAB ALQ=500, FOP=CBT, FAC=<PUT>, - 
              FNM=<DISK$:[PROGRAM]SAMPLE_FILE.DAT>, - 
              ORG=SEQ, RAT=CR, RFM=VAR, SHR=<NIL>, MRS=52, XAB=MYXDAT 
 
   .
   .
   .

As described in the section on the XAB$L_ALQ field (in the same manual), this parameter sets the FAB field FAB$L_ALQ. To perform the same initialization in Compaq Fortran, you must supply a value to the FAB$L_ALQ field using a run-time assignment statement. For example:


  MYFAB.FAB$L_ALQ = 500 

The FAB$B_BID and FAB$B_BLN fields must be filled in by your program prior to their use in an RMS service call, unless they have already been supplied by the Compaq Fortran RTL I/O routines. You should always use the symbolic names for the values of these fields; for example:


  INCLUDE '($FABDEF)' 
  ...
  RECORD /FABDEF/ MYFAB 
  ...
  MYFAB.FAB$B_BID = FAB$C_BID 
  MYFAB.FAB$B_BLN = FAB$C_BLN 
  ...
  STATUS = SYS$OPEN( ... ) 
  ...

11.1.3 The Record Access Block

The Record Access Block (RAB) is used for calling the following services:
SYS$CONNECT SYS$READ
SYS$DELETE SYS$RELEASE
SYS$DISCONNECT SYS$REWIND
SYS$FIND SYS$SPACE
SYS$FLUSH SYS$TRUNCATE
SYS$FREE SYS$UPDATE
SYS$GET SYS$WAIT
SYS$NXTVOL SYS$WRITE
SYS$PUT  

The purpose of the RAB is to describe the record being manipulated by these services. The RAB contains a pointer to the FAB used to open the file being manipulated, making it unnecessary for these services to have a FAB in their argument lists. Also, a RAB can point to certain types of XABs.

Using the FOR$RAB Intrinsic Function

To declare the structure and parameter values for using RAB blocks, include the $RABDEF library module from FORSYSDEF. For example:


  INCLUDE '($RABDEF)' 

To examine the fields and values declared, use the /LIST qualifier after the right parenthesis. Each field in the RAB is described at length in the OpenVMS Record Management Services Reference Manual.

If you are using a USEROPEN procedure, the actual allocation of the RAB is performed by the Compaq Fortran Run-Time Library I/O support routines, and you only need to declare the second argument to your USEROPEN routine to be a record with the RAB structure. For example:

Calling program:


  ...
  EXTERNAL MYOPEN 
  ...
  OPEN (UNIT=8, ..., USEROPEN=MYOPEN) 
  ...

USEROPEN routine:


  INTEGER FUNCTION MYOPEN(FABARG, RABARG, LUNARG) 
  ...
  INCLUDE '($RABDEF)' 
  ...
  RECORD /RABDEF/ RABARG 
  ...

If you need to access the RAB used by the Fortran I/O system for one of the open files in your program, you can use the FOR$RAB intrinsic function (do not declare it as EXTERNAL). You can use FOR$RAB even if you did not use a USEROPEN routine to open the file. The FOR$RAB intrinsic function takes a single INTEGER*4 variable (or constant) argument, the unit number of the open file for which you want to obtain the RAB address. The function result is the address of the RAB for that unit.

If you use the FOR$RAB function in your program, you should declare it to be INTEGER*4 if you assign the result value to a variable. If you do not, your program will assume that it is a REAL function and will perform an improper conversion to INTEGER.

To use the result of the FOR$RAB call, you must pass it to a subprogram as an actual argument using the %VAL built-in function. This allows the subprogram to access it as an ordinary Compaq Fortran record argument. For example, the main program for calling a subroutine to print the RAB fields could be coded as follows:


  INTEGER (KIND=4) RABADR, FOR$RAB 
  ...
  OPEN(UNIT=14, FILE='TEST.DAT', STATUS='OLD') 
  ...
  RABADR = FOR$RAB(14) 
  ...
  CALL DUMPRAB(%VAL(RABADR)) 
  ...

If you need to access other control blocks in use by the RMS services for that unit, you can obtain their addresses using the link fields they contain. For example:


  SUBROUTINE DUMPRAB(RAB) 
  ...
  INTEGER (KIND=4)  FABADR 
  INCLUDE '($RABDEF)' 
  RECORD /RABDEF/ RAB 
  ...
  FABADR = RAB.RAB$L_FAB 
  ...
  CALL DUMPFAB(%VAL(FABADR)) 
  ...

In this example, the routine DUMPRAB obtains the address of the associated FAB by referencing the RAB$L_FAB field of the RAB. Other control blocks associated with the FAB, such as the NAM and XAB blocks, can be accessed using code similar to this example.

Usually, you need to declare only one RAB block. Sometimes, however, you may need to use more than one. For example, the multistream capability of RMS allows you to connect several RABs to a single FAB. This allows you to simultaneously access several records of a file, keeping a separate context for each record. You can declare whatever RAB blocks you need with a RECORD statement. For example:


  INCLUDE '($RABDEF)' 
  ...
  RECORD /RABDEF/ RAB1, RABARRAY(10) 

If you use any of the above service calls without using a USEROPEN routine, you must initialize the required RAB fields in your program. The RAB fields required for each RMS service are listed in the descriptions of individual services in the OpenVMS Record Management Services Reference Manual. Most services also fill in output values in the RAB. Descriptions of these output fields are provided with the service descriptions.

In the example programs supplied in the OpenVMS Record Management Services Reference Manual, these initial field values are described as they would be used in MACRO programs, where the declaration macros allow initialization arguments. Thus, in each case where the MACRO example shows a field being initialized in a declaration macro, you must have a corresponding initialization at run time in your program.

For example, the OpenVMS Record Management Services Reference Manual contains an example that shows the use of the RAC parameter for specifying the record access mode to use:


  . 
  . 
  . 
  
  SRC_FAB: 
          $FAB    FAC=<GET>,-           ; File access for GET only 
                  FOP=<SQO>,-           ; DAP file transfer mode 
                  FNM=<SRC:>            ; Name of input file 
  SRC_RAB: 
          $RAB    FAB=SRC_FAB,-         ; Address of associated FAB 
                  RAC=SEQ,-             ; Sequential record access 
                  UBF=BUFFER,-          ; Buffer address 
                  USZ=BUFFER_SIZE       ; Buffer size 
 
  .
  .
  .

In this example, sequential access mode is used. As described in the section on the RAC field (in the same manual), this parameter sets the RAB$B_RAC field to the value RAB$C_SEQ. This means that to perform the same initialization in Fortran, you must supply RAC field values by a run-time assignment statement. For example:


  MYRAB.RAB$B_RAC = RAB$C_SEQ 

The RAB$B_BID and RAB$B_BLN fields must be filled in by your program prior to their use in an RMS service call, unless they have been supplied by the Fortran RTL I/O routines. You should always use the symbolic names for the values of these fields. For example:


  INCLUDE '($RABDEF)' 
  ...
  RECORD /RABDEF/ MYRAB 
  ...
  MYRAB.RAB$B_BID = RAB$C_BID 
  MYRAB.RAB$B_BLN = RAB$C_BLN 
  ...
  STATUS = SYS$CONNECT(MYRAB) 
  ...

11.1.4 The Name Block

The Name Block (NAM) can be used with the FAB in most FAB-related services in order to supply to or receive from RMS more detailed information about a file name. The NAM block is never given directly as an argument to an RMS service; to supply it you must link to it from the FAB. See Section 11.1.1 for an example of this.

To declare the structure and parameter values for using NAM blocks, include the $NAMDEF library module from FORSYSDEF. For example:


  INCLUDE '($NAMDEF)' 

To examine the fields and values declared, use the /LIST qualifier after the right parenthesis. Each field in the NAM is described in the OpenVMS Record Management Services Reference Manual.

If you are using a USEROPEN procedure, the actual allocation of the NAM is performed by the Compaq Fortran Run-Time Library I/O support routines. Because the NAM block is linked to the FAB, it is not explicitly given in the USEROPEN routine argument list.

To access the NAM, you need to call a subprogram, passing the pointer by value and accessing the NAM in the subprogram as a structure. For example:

Calling program:


  ...
  EXTERNAL MYOPEN 
  ...
  OPEN (UNIT=8, ..., USEROPEN=MYOPEN) 
  ...

USEROPEN routine:


  INTEGER FUNCTION MYOPEN(FABARG, RABARG, LUNARG) 
  ...
  INCLUDE '($FABDEF)' 
  ...
  RECORD /FABDEF/ FABARG 
  ...
  CALL NAMACCESS(%VAL(FABARG.FAB$L_NAM)) 
  ...

NAM accessing routine:


  SUBROUTINE NAMACCESS(NAMARG) 
  ...
  INCLUDE '($NAMDEF)' 
  ...
  RECORD /NAMDEF/ NAMARG 
  ...
  IF (NAMARG.NAM$B_ESL .GT. 132) GO TO 100 
  ...

Usually, you only need to declare one NAM block. You can declare whatever NAM blocks you need with a RECORD statement. For example:


  INCLUDE '($NAMDEF)' 
  ...
  RECORD /NAMDEF/ NAM1, NAM2 

Most often, you use the NAM block to pass and receive information about the components of the file specification, such as the device, directory, file name, and file type. For this reason, most of the fields of the NAM block are CHARACTER strings and lengths. When using the NAM block, you should be familiar with the argument passing mechanisms for CHARACTER arguments described in Section 10.7.4.3.

Your program must fill in the NAM$B_BID and NAM$B_BLN fields prior to their use in an RMS service call, unless they have been supplied by the Compaq Fortran RTL I/O routines. You should always use the symbolic names for the values of these fields. For example:


  INCLUDE '($NAMDEF)' 
  ...
  RECORD /NAMDEF/ MYNAM 
  ...
  MYNAM.NAM$B_BID = NAM$C_BID 
  MYNAM.NAM$B_BLN = NAM$C_BLN 
  ...

11.1.5 Extended Attributes Blocks

Extended Attribute Blocks (XABs) are a family of related structures for passing and receiving additional information about files. There are different kinds of XABs:

The XABs are described in the OpenVMS Record Management Services Reference Manual. XABs are generally smaller and simpler than the FAB, RAB, and NAM blocks because each describes information about a single aspect of the file. You do not have to use all of them; for any given call to an RMS service routine, use only those that are required.

Often the XAB fields override the corresponding fields in the FAB. For example, the allocation XAB describes the file's block allocation in more detail than the FAB$L_ALQ field can. For this reason, XAB$L_ALQ (the allocation field in the XABALL structure) always overrides the FAB$L_ALQ value.

The XABs used for any given RMS service call are connected to the FAB in a linked list. The head of the list is the FAB$L_XAB field in the FAB. This field contains the address of the first XAB to be used. Each successive XAB in the list links to the next using the XAB$L_NXT field. This field contains the address of the next XAB in the list. The order of the XABs in the list does not matter, but each kind of XAB must not appear more than once in the list.

The only kind of XAB that can be connected to a RAB instead of a FAB is the terminal XAB. It is linked to the RAB with the RAB$L_XAB field. This is needed because the terminal control information is dynamic and potentially changes with each record operation performed.

To declare the structure and parameter values for using the different XAB blocks, include the appropriate XAB definition library module from FORSYSDEF. (The names of the XAB definition library modules are listed previously in this section.) Also, because the XABs are a family of related control blocks, you need to include the $XABDEF library module from FORSYSDEF.TLB in order to declare the fields common to all XABs. For example, to declare the fields used in the Date and Time XAB, use the following declarations:


  INCLUDE '($XABDATDEF)' 
  INCLUDE '($XABDEF)' 

To examine the fields and values declared, use the /LIST qualifier after the right parenthesis. All of the fields in the XABs are described in the OpenVMS Record Management Services Reference Manual.

If you are using a USEROPEN procedure, the actual allocation of the XABs used by the open operation is performed by the Compaq Fortran Run-Time Library I/O support routines. Because the XAB blocks are linked to the FAB, the XAB block addresses are not explicitly given in the USEROPEN routine argument list.

To access the XABs, you need to call a subprogram and pass a pointer to it using the %VAL built-in function or a pointer argument. For an example of this method, see Section 11.1.3.

To allocate space for an XAB block in your program, you need to declare it with a RECORD statement. For example:


  INCLUDE '($XABDATDEF)' 
  INCLUDE '($XABPRODEF)' 
  ...
  RECORD /XABDATDEF/ MYXABDAT, /XABPRODEF/ MYXABPRO 
  ...

For each XAB that you declare in your program, you must supply the correct COD and BLN fields explicitly. These field offsets are common to all XABs and are contained in the $XABDEF library module in FORSYSDEF.TLB. The block id and length are unique for each kind of XAB and the symbolic values for them are contained in the separate XAB declaration library modules in FORSYSDEF.TLB. For example, to properly initialize a Date and Time XAB, you could use the following code segment:


  INCLUDE '($XABDEF)' 
  INCLUDE '($XABDATDEF)' 
  RECORD /XABDEF/ MYXABDAT 
  ...
  MYXABDAT.XAB$B_COD = XAB$C_DAT 
  MYXABDAT.XAB$B_BLN = XAB$C_DATLEN 
  ...

11.2 RMS Services

In general, you need to do the same things when calling an RMS service that you need to do when calling any OpenVMS service: declare the name, pass arguments, and check status values. However, RMS services have some additional conventions and ease-of-use features that you should be aware of.

For More Information:

11.2.1 Declaring RMS System Service Names

As with the other system services, you should use the $SYSSRVNAM library module in FORSYSDEF to declare the names of all of the RMS services. For example:


  INCLUDE '($SYSSRVNAM)' 

This library module contains comments describing each OpenVMS system service, including all of the RMS services, and INTEGER*4 and EXTERNAL declarations for each. Including the library module allows you to use the names of the RMS services in your programs without further declaration.

11.2.2 Arguments to RMS Services

Most RMS services require three arguments:

Some RMS services take other arguments, but these services are rarely needed. You should always refer to the documentation for the specific service that you are calling for detailed information on its arguments.

Most RAB and FAB fields are ignored by most RMS services. The documentation of each service in the OpenVMS Record Management Services Reference Manual describes which fields are input for that service and which are output, for each control block used. Services that take a FAB as an argument are called the File Control services. Services that take a RAB as an argument are called the Record Control services. Typically, you need to use both when doing RMS I/O in your program.

In general, fields that are not documented as required for input to a service are ignored and can be left uninitialized. The exceptions are the Block Id (BID or COD) and Block Length (BLN) fields; these must always be initialized. See the preceding sections about the respective blocks for examples of how to initialize these fields.

The output of many RMS services provides the values required for input to other RMS services. For this reason, you usually only need to initialize a few fields in each block to their nondefault values. This is especially true when using RMS blocks declared with the Compaq Fortran RTL I/O routines as when using USEROPEN routines or the FOR$RAB function.

For More Information:

About passing arguments to system services, see Section 10.7.4.

11.2.3 Checking Status from RMS Services

You should always invoke RMS services as functions, rather than calling them as subroutines (see Section 10.7.2 for a general discussion of this topic). It is particularly important to check the status of RMS services because they usually do not cause an error when they fail. If the status is not checked immediately, the failure will go undetected until later in the program where it will be difficult to diagnose.

In most cases, you only need to check for success or failure by testing whether the returned status is true or false. Some services have alternate success-status possibilities. You should always check for these in cases where the program depends on the correct operation of the services.

The RMS services have a unique set of status return symbols not used by any of the other OpenVMS system services. You should always use these symbols whenever you check the individual status values returned. To obtain the declarations for these symbols, include the $RMSDEF library module from FORSYSDEF.TLB. For example:


  INCLUDE '($RMSDEF)' 

This statement includes in your program the declarations for all of the symbolic RMS return values.

The OpenVMS Record Management Services Reference Manual documents the symbolic values, both success and failure, that can be returned from each of the services. Your program should always test each service-result status against these symbolic values and take appropriate action when a failure status is detected. You should always declare status variables as INTEGER*4 type in order to avoid unexpected numeric conversions. The recommended action depends on whether you are using RMS services in a USEROPEN routine.

The Compaq Fortran RTL I/O routines that invoke your USEROPEN routine are expecting an RMS status as an output value. For this reason, you need to return the RMS status value as the function value for both failure and success conditions. For example:


  INTEGER FUNCTION MYOPEN(FAB,RAB,LUN) 
  ...
  INCLUDE '($SYSSRVNAM)'         ! Declare RMS service names 
  ...
  MYOPEN = SYS$OPEN(FAB) 
  IF (.NOT. MYOPEN) RETURN 
  ...
RETURN 
END 

In this case, if the SYS$OPEN service fails, it returns an error status into the function result variable MYOPEN. If the test of MYOPEN does not indicate success, the function returns the actual RMS status as its value. Then the RTL I/O routines will signal the appropriate Fortran error normally, as if a USEROPEN routine had not been used.

If the SYS$OPEN call succeeds, the program continues, and the RMS$_NORMAL success status will ultimately be returned to the Fortran RTL. This value will cause the OPEN statement that specifies MYOPEN to complete successfully.

If you are not using a USEROPEN routine, your program must indicate the error status directly, unless it is prepared to deal with it. Often, the easiest way to indicate an error and issue a helpful message is to signal the RMS condition directly with LIB$SIGNAL or LIB$STOP. For example:


  ...
  INCLUDE '($SYSSRVNAM)'         ! Declare RMS service names 
  INTEGER (KIND=4) STATUS        ! Declare a status variable 
  ...
  STATUS = SYS$GET(MYRAB) 
  IF (.NOT. STATUS) CALL LIB$STOP(%VAL(STATUS)) 

For More Information:

On the use of LIB$SIGNAL and LIB$STOP, see Chapter 14.

11.2.4 Opening a File

To perform input or output operations on a file, your program must first open the file and establish an active RMS I/O stream. To open a file, your program generally needs to call either the SYS$CREATE or SYS$OPEN services, followed by the SYS$CONNECT service. When your program uses an OPEN statement without a USEROPEN routine, the Compaq Fortran RTL I/O routines call these RMS services.

Options related to opening a file follow:

The value of the FAB$B_FAC field of the FAB indicates to RMS what record operations are to be done on the file being opened. If a record operation that was not indicated by the FAC field (such as a SYS$PUT) is attempted, the record service will not perform the operation and will return a failure status. This file protection feature prevents you from accidentally corrupting a file when you use the wrong RMS service.

The SYS$CONNECT service establishes an active I/O stream, using a RAB, to a file that has been previously opened by your program. RMS identifies all active I/O streams by a unique identifier, called the Internal Stream Identifier (ISI). This value is stored in the RAB$W_ISI field of the RAB for each active stream being processed.

This field must always be zero when calling SYS$CONNECT. The SYS$CONNECT service initializes this field, so that subsequent operations using that RAB can be uniquely identified. Under some circumstances, you can establish more than one simultaneously active I/O stream to the same file. See the OpenVMS Record Management Services Reference Manual for more information on this topic.

11.2.5 Closing a File

To close a file, use the SYS$CLOSE service. This terminates all active I/O streams under way on that file and frees all RMS resources being used for processing that file. Use the SYS$DISCONNECT service if you want to end one active I/O stream, but continue processing the file using another stream. This service sets the RAB$W_ISI value to zero so that the RAB can be reused for another stream.

11.2.6 Writing Data

To write data to a file, use the SYS$PUT or SYS$WRITE service. Your program must set the PUT bit in the FAB$B_FAC field when the file is opened; otherwise, the service attempting the write operation will fail.

Use the SYS$PUT service when you want to write data in record mode (the default). In record mode, RMS buffers data automatically and performs the actual output operation for a whole group of records at a time. This is the mode used for all Compaq Fortran WRITE statements. Because most programs and utilities can read data written in record mode, this mode should be used when the data being written is to be read and processed by a general program or utility.

Use the SYS$WRITE service when you want to bypass the record management capabilities of RMS and write blocks of data directly to the device without additional buffering. This mode is called block mode I/O and is generally much faster and uses less CPU resources than record mode. For this reason, it is the preferred mode for writing large amounts of unformatted data to a device.

Block mode should only be used when the program that needs to read the data can also use block mode. If the program that is to read the data cannot use block mode, you must use some other means to guarantee that the data being written can be accessed. For instance, it is not generally possible to read data written with SYS$WRITE using ordinary Compaq Fortran READ statements. Before using SYS$WRITE, read the special restrictions on using block mode in the OpenVMS Record Management Services Reference Manual, because SYS$WRITE (block mode) may have different device dependencies than SYS$PUT (record mode).

11.2.7 Reading Data

To read data from a file, use the SYS$GET or SYS$READ service. Your program must set the GET bit in the FAB$B_FAC field when the file is opened; otherwise, the service attempting the read operation will fail.

Use the SYS$GET service when you want to read data in record mode (the default). In this mode, RMS buffers data automatically and performs the actual input operation for a whole group of records at a time. This is the mode used for all Compaq Fortran READ statements. This mode should be used whenever the program or utility that wrote the data used record mode, unless your reading program can buffer and deblock the data itself.

Use the SYS$READ service when you want to read data using block mode I/O (see Section 11.2.6. Using SYS$READ is the preferred mode for reading large amounts of unformatted data from a device, but it should only be used when the data was written by a utility or program that wrote the data in block mode.

If the file was written using record mode, RMS control information may be intermixed with the data, making it difficult to process. Before using SYS$READ, read the special restrictions on using block mode in the OpenVMS Record Management Services Reference Manual, because SYS$READ (block mode) may have different device dependencies than SYS$GET (record mode).

11.2.8 Other Services

RMS provides many other file and record processing services beyond just Opening, Closing, Reading and Writing. Other file processing services include the following:

Other record processing services include the following:

For More Information:

On the RMS services, see the OpenVMS Record Management Services Reference Manual.

11.3 User-Written Open Procedures

The USEROPEN keyword in an OPEN statement provides you with a way to access RMS facilities that are otherwise not available to Compaq Fortran programs. It specifies a user-written external procedure (USEROPEN procedure) that controls the opening of a file. The USEROPEN keyword has the form:

USEROPEN = procedure-name

The procedure-name represents the symbolic name of a user-written open procedure. The procedure must be declared in an EXTERNAL statement, and should be a FUNCTION that returns an INTEGER*4 result.

When an OPEN statement---with or without the USEROPEN keyword---is executed, the Run-Time Library uses the OPEN statement keywords to establish the RMS File Access Block (FAB) and the Record Access Block (RAB), as well as its own internal data structures. If a USEROPEN keyword is included in the OPEN statement, the Run-Time Library then calls your USEROPEN procedure instead of opening the file according to its normal defaults. The procedure can then provide additional parameters to RMS and can obtain results from RMS.

The three arguments passed to a user-written open procedure by the Run-Time Library are as follows:

  1. The address of the FAB
  2. The address of the RAB
  3. The address of a longword integer containing the unit number

Using this information, your USEROPEN procedure can then perform the following operations:

A USEROPEN routine can set FAB fields before the corresponding file is opened (or created). However, once the file is open (except if the FAB$V_UFO bit is set), the user should not alter any of the FAB fields.

A USEROPEN routine can likewise set RAB fields before the record stream has been connected (SYS$CONNECT). However, once the file is connected to the record stream, the user should not alter any of the RAB fields.

Once a FAB or RAB is used by the Fortran RTL, it should be treated as read-only by the user, because the Fortran RTL may need to set and alter those fields as needed to complete the user Fortran I/O statements. Any user modification of a FAB or RAB after its initial use may not have the intended effect for subsequent Fortran I/O statements.

For More Information:

About the FAB and RAB, see the OpenVMS Record Management Services Reference Manual.

11.3.1 Examples of USEROPEN Routines

The following OPEN statement either creates a 1000-block contiguous file or returns an error. (The default Compaq Fortran interpretation of the INITIALSIZE keyword is to allocate the file contiguously on a best-effort basis, but not to generate an error if the space is not completely contiguous.)


  EXTERNAL CREATE_CONTIG 
  OPEN (UNIT=10, FILE='DATA', STATUS='NEW', INITIALSIZE=1000, & 
    USEROPEN=CREATE_CONTIG) 

User-written open procedures are often coded in BLISS or MACRO; however, they can also be coded in Compaq Fortran using Compaq Fortran's record handling capability.

The following function creates a file after setting the RMS FOP bit (FAB$V_CTG) to specify contiguous allocation.


!       UOPEN1 
! 
! Program to demonstrate the use of a simple USEROPEN routine 
! 
  
  PROGRAM UOPEN1 
  EXTERNAL CREATE_CONTIG 
 
! OPEN the file specifying the USEROPEN routine 
 
  OPEN (UNIT=10, FILE='DATA', STATUS='NEW', INITIALSIZE=1000, & 
    USEROPEN=CREATE_CONTIG) 
 
  STOP 
  END PROGRAM UOPEN1 
 
! CREATE_CONTIG 
 
! Sample USEROPEN function to force RMS to allocate contiguous 
! blocks for the initial creation of a file. 
 
  INTEGER FUNCTION CREATE_CONTIG(FAB,RAB,LUN) 
  
!       Required declarations 
 
  INCLUDE '($FABDEF)'              ! FAB Structure 
  INCLUDE '($RABDEF)'              ! RAB Structure 
  INCLUDE '($SYSSRVNAM)'           ! System service name declarations 
  RECORD /FABDEF/ FAB, /RABDEF/ RAB 
 
! Clear the "Contiguous-best-try" bit, set the "Contiguous" bit 
 
  FAB.FAB$L_FOP = FAB.FAB$L_FOP .AND. .NOT. FAB$M_CBT 
  FAB.FAB$L_FOP = FAB.FAB$L_FOP .OR. FAB$M_CTG 
 
!       Perform the create and connect, and return status 
 
  CREATE_CONTIG = SYS$CREATE(FAB) 
  IF (.NOT. CREATE_CONTIG) RETURN 
  CREATE_CONTIG = SYS$CONNECT(RAB) 
  RETURN 
  END FUNCTION CREATE_CONTIG 

11.3.2 RMS Control Structures

Use of the USEROPEN keyword has some restrictions. The Run-Time Library constructs the following RMS control structures before calling the USEROPEN procedure:
FAB File Access Block
RAB Record Access Block
NAM Name Block
XAB Extended Attributes Blocks
ESA Expanded String Area
RSA Resultant String Area

A USEROPEN procedure should not alter the allocation of these structures, although it can modify the contents of many of the fields. Your procedure can also add additional XAB control blocks by linking them anywhere into the XAB chain. You must exercise caution when changing fields that have been set as a result of Compaq Fortran keywords, because the Run-Time Library may not be aware of the changes. For example, do not attempt to change the record size in your USEROPEN procedure; instead, use the Compaq Fortran keyword RECL. Always use an OPEN statement keyword if one is available.

Although the FAB, RAB, and NAM blocks remain defined during the time that the unit is opened, the XAB blocks are present only until the file has been successfully opened. The locations of the ESA and RSA strings are changed after the file is opened, so your USEROPEN procedure should not store the addresses of the RMS control structures. Instead, have your program call FOR$RAB to obtain the address of the RAB once the file is opened and then access the other structures through the RAB.

Note

Future releases of the Run-Time Library may alter the use of some RMS fields. Therefore, you may have to alter your USEROPEN procedures accordingly.

Table 11-1 shows which FAB and RAB fields are either initialized before your USEROPEN procedure is called or examined upon return from your USEROPEN procedure. All fields are initialized in response to OPEN statement keywords or default to zero. Fields labeled with a hyphen (-) are initialized to zero. Fields labeled with an asterisk (*) are returned by RMS.

Table 11-1 RMS Fields Available with USEROPEN
Field Name Description Compaq Fortran OPEN Keyword and Value
FAB$B_ACMODES File access modes Contains FAB$V_CHAN_MODE and FAB$V_LNM_MODE.
FAB$L_ALQ Allocation quantity n if INITIALSIZE=n
FAB$B_BKS Bucket size (BLOCKSIZE + 511)/512
FAB$W_BLS Block size n if BLOCKSIZE=n
FAB$V_CHAN_MODE Channel access mode protection (2-bit subfield in FAB$B_ACMODES) 0=usermode
FAB$L_CTX Context --
FAB$W_DEQ Default file extension quantity n if EXTENDSIZE=n
FAB$L_DEV Device characteristics *
FAB$L_DNA Default file specification string
address
UNIT= nnn
Set to FOR nnn.DAT or FORREAD.DAT, FORACCEPT.DAT, FORTYPE.DAT, or FORPRINT.DAT or to default file specification string
FAB$B_DNS Default file specification string size Set to length of default file specification string
FAB$B_FAC File access READONLY
Set to 0 if READONLY (RMS default), else set to FAB$M_GET + FAB$M_PUT + FAB$M_UPD + FAB$M_TRN + FAB$M_DEL
FAB$L_FNA File specification string address FILE=filename if FILE present, else set to FOR nnn, FOR$READ, FOR$ACCEPT, FOR$TYPE, FOR$PRINT, SYS$INPUT, or SYS$OUTPUT
FAB$B_FNS File specification string size Set to length of file specification string
FAB$L_FOP File processing options  
  FAB$V_ASY Asynchronous operation -- (not used)
  FAB$V_CBT Contiguous best try 1 if INITIALSIZE=n
  FAB$V_CIF Create if nonexistent 1 if READONLY not specified and STATUS='UNKNOWN' or STATUS omitted
  FAB$V_CTG Contiguous allocation --
  FAB$V_DFW Deferred write 1
  FAB$V_DLT Delete on close service Set at Fortran close, depending upon DISP keyword in OPEN or CLOSE, or STATUS keyword in CLOSE
  FAB$V_KFO Known file open --
  FAB$V_MXV Maximize version number --
  FAB$V_NAM Name block inputs --
  FAB$V_NEF Not positioned at end of file 1 unless ACCESS= 'APPEND'
  FAB$V_NFS Not file structured --
  FAB$V_OFP Output file parse --
  FAB$V_POS Current position (after closed file) --
  FAB$V_PPF Process permanent file --
  FAB$V_RCK Read check --
  FAB$V_RWC Rewind on close service --
  FAB$V_RWO Rewind on open service --
  FAB$V_SCF Submit command (when closed) Set at Fortran close, depending upon DISP keyword in OPEN or CLOSE, or STATUS keyword in CLOSE
  FAB$V_SPL Spool to printer Set at Fortran close, depending upon DISP keyword in OPEN or CLOSE, or STATUS keyword in CLOSE
  FAB$V_SQO Sequential only 1 if a network file and ACCESS='SEQUENTIAL' or 'APPEND', else 0
  FAB$V_SUP Supersede --
  FAB$V_SYNCSTS Immediate asynchronous completion * (not used)
  FAB$V_TEF Truncate at end-of-file --
  FAB$V_TMD Temporary, marked for delete 1 if STATUS= 'SCRATCH', else 0
  FAB$V_TMP Temporary (file with no directory
entry)
--
  FAB$V_UFO User file open or create file only --
  FAB$V_WCK Write check --
FAB$B_FSZ Fixed control area size --
FAB$W_IFI Internal file identifier *
FAB$W_GBC Global buffer count --
FAB$B_JOURNAL Journal flags status --
FAB$V_LNM_MODE Logical name translation access mode (subfield in FAB$B_ACMODES) --
FAB$L_MRN Maximum record number n if MAXREC=n
FAB$W_MRS Maximum record size n if RECORDTYPE='FIXED' or ORGANIZATION='RELATIVE' or ='INDEXED', else 0
FAB$L_NAM Name block address Set to address of name block; both the expanded and resultant string areas are set up, but the related filename string is not
FAB$B_ORG File organization FAB$C_IDX if ORGANIZATION='INDEXED'
FAB$C_REL if ORGANIZATION='RELATIVE'
FAB$C_SEQ if ORGANIZATION='SEQUENTIAL' or omitted
FAB$B_RAT Record attributes  
  FAB$V_FTN Fortran carriage control 1 if CARRIAGECONTROL='FORTRAN' or not specified
  FAB$V_CR Print LF and CR 1 if CARRIAGECONTROL='LIST'
  FAB$V_BLK Do not cross block boundaries -- (1 if NOSPANBLOCKS)
FAB$B_RFM Record format FAB$C_FIX if RECORDTYPE='FIXED'
FAB$C_VAR if RECORDTYPE='VARIABLE'
FAB$C_VAR if RECORDTYPE='SEGMENTED'
FAB$C_STM if RECORDTYPE='STREAM'
FAB$C_STMCR if RECORDTYPE='STREAM_CR'
FAB$C_STMLF if RECORDTYPE='STREAM_LF'
FAB$B_RTV Retrieval window size --
FAB$L_SDC Spooling device characteristics *
FAB$B_SHR File sharing  
  FAB$V_SHRPUT Allow other PUTs 1 if SHARED
  FAB$V_SHRGET Allow other GETs 1 if SHARED
  FAB$V_SHRDEL Allow other DELETEs 1 if SHARED
  FAB$V_SHRUPD Allow other UPDATEs 1 if SHARED
  FAB$V_NIL Allow no other operations --
  FAB$V_UPI User-provided interlock --
  FAB$V_MSE Multistream allowed --
FAB$L_STS Completion status code *
FAB$L_STV Status value *
FAB$L_XAB Extended attribute block address The XAB chain always has a File Header Characteristics (FHC) extended attribute block in order to get longest record length (XAB$W_LRL). If the KEY=keyword is specified, key index definition blocks will also be present. Compaq may add additional XABs in the future. Your USEROPEN procedure may insert XABs anywhere in the chain.
RAB$L_BKT Bucket code --
RAB$L_CTX Context --
RAB$L_FAB FAB address Set to address of FAB
RAB$W_ISI Internal stream ID *
RAB$L_KBF Key buffer address Set to address of longword containing logical record number if ACCESS='DIRECT'
RAB$B_KRF Key of reference 0
RAB$B_KSZ Key size --
RAB$B_MBC Multiblock count If BLOCKSIZE=n, use (n + 511)/512
RAB$B_MBF Multibuffer count n if BUFFERCOUNT=n
RAB$L_PBF Prompt buffer address --
RAB$B_PSZ Prompt buffer size --
RAB$B_RAC Record access mode  
  RAB$C_KEY If ACCESS='DIRECT' or 'KEYED'  
  RAB$C_SEQ If ACCESS='SEQUENTIAL' or 'APPEND', or ACCESS omitted  
  RAB$C_RFA --  
RAB$L_RBF Record buffer address Set later
RAB$L_RFA Record file address Set later
RAB$L_RHB Record header buffer --
RAB$L_ROP Record processing options  
  RAB$V_ASY Asynchronous --
  RAB$V_BIO Block I/O --
  RAB$V_CCO Cancel CTRL/O --
  RAB$V_CVT Convert to uppercase --
  RAB$V_EOF End-of-file 1 if ACCESS='APPEND'
  RAB$V_ETO Extended terminal (XABTRM) operation --
  RAB$V_FDL Fast delete --
  RAB$V_KGE or
RAB$V_EQNXT
Key greater than or equal to --
  RAB$V_KGT or
RAB$V_NXT
Key greater than --
  RAB$V_LIM Limit --
  RAB$V_LOA Load buckets according to fill size --
  RAB$V_LOC Locate mode 1
  RAB$V_NLK No lock --
  RAB$V_NXR Nonexistent record --
  RAB$V_PMT Prompt --
  RAB$V_PTA Purge type-ahead --
  RAB$V_RAH Read-ahead 1
  RAB$V_REA Lock record for read --
  RAB$V_RLK Lock record for write --
  RAB$V_RNE Read no echo --
  RAB$V_RNF Read no filter --
  RAB$V_RRL Read regardless of lock --
  RAB$V_SYNCSTS Immediate asynchronous completion * (not used)
  RAB$V_TMO Timeout --
  RAB$V_TPT Truncate on PUT 1
  RAB$V_UIF Update if 1 if ACCESS='DIRECT'
  RAB$V_ULK Manual unlocking --
  RAB$V_WAT Wait for locked record --
  RAB$V_WBH Write-behind 1
RAB$W_RSZ Record size Set later
RAB$L_STS Completion status code *
RAB$L_STV Status value *
RAB$B_TMO Timeout period --
RAB$L_UBF User record area address Set later
RAB$W_USZ User record area size Set later
RAB$L_XAB Extended attribute block address The XAB chain allows you to set or obtain additional information about an I/O operation.

RMS does not allow multiple instances of the same type XAB. To be compatible with future releases of the Run-Time Library, your procedure should scan the XAB chain for XABs of the type to be inserted. If one is found, it should be used instead.

11.4 Example of Block Mode I/O

The following example shows a complete application that calls the RMS block I/O services SYS$WRITE and SYS$READ directly from Compaq Fortran. A complete program called BIO.F90 writes out an array of REAL*8 values to a file using SYS$WRITE, closes the file, and then reads the data back in using SYS$READ operations with a different I/O transfer size. This program consists of five routines:
BIO Main control program
BIOCREATE USEROPEN routine to create the file
BIOREAD USEROPEN routine to open the file for READ access
OUTPUT Function that actually outputs the array
INPUT Function that actually reads the array and checks it

11.4.1 Main Block Mode I/O Program---BIO

The following main program specifies the USEROPEN specifier in its OPEN statements.


!  File: BIO.F90 
! 
!       Program to demonstrate the use of RMS Block I/O operations 
!       from Compaq Fortran 
 
   PROGRAM BIO 
 
!  Declare the Useropen routines as external 
 
   EXTERNAL BIOCREATE, BIOREAD 
 
!  Declare status variable, functions, and unit number 
 
   LOGICAL (KIND=4)  STATUS, OUTPUT, INPUT 
   INTEGER (KIND=4) IUN/1/ 
 
!       Open the file                                   (1)
 
   OPEN(UNIT=IUN, FILE='BIODEMO.DAT', FORM='UNFORMATTED', & 
     STATUS='NEW', RECL=128, BLOCKSIZE=512, ORGANIZATION='SEQUENTIAL', & 
     IOSTAT=IOS,  ACCESS='SEQUENTIAL', RECORDTYPE='FIXED',  & 
     USEROPEN=BIOCREATE, INITIALSIZE=100) 
 
   IF (IOS .NE. 0) STOP 'Create failed'                 (2)
 
!       Now perform the output 
 
   STATUS = OUTPUT(%VAL(FOR$RAB(IUN)))                  (3)
   IF (.NOT. STATUS) STOP 'Output failed'               (2)
 
!       Close the file for output 
 
   CLOSE (UNIT=IUN) 
 
!  Confirm output complete 
 
   TYPE *, 'Output complete, file closed' 
 
!  Now open the file for input                          (2)
 
   OPEN(UNIT=IUN, FILE='BIODEMO.DAT', FORM='UNFORMATTED',  & 
     STATUS='OLD', IOSTAT=IOS, USEROPEN=BIOREAD, DISP='DELETE') 
  
   IF (IOS .NE. 0) STOP 'Open for read failed'          (2)
 
!       Now read the file back 
 
   STATUS = INPUT(%VAL(FOR$RAB(IUN)))                   (3)
   IF (.NOT. STATUS) STOP 'Input failed'                (2)
 
!       Success, output that all is well 
 
   STOP 'Correct completion of Block I/O demo' 
   END PROGRAM BIO 
 

  1. Most of the necessary OPEN options for the file are specified with OPEN statement parameters. This is recommended whenever an OPEN statement qualifier exists to perform the desired function because it allows the Compaq Fortran RTL I/O processing routines to issue appropriate error messages when an RMS routine returns an error status.
    Note the discrepancy between RECL and BLOCKSIZE in the first OPEN statement. Both keywords specify 512 bytes, but the number given for RECL is 128. This is because the unit implied in the RECL keyword is longwords for unformatted files.
    When using Block I/O mode, the blocksize used in the I/O operations is determined by the routine that actually does the operation. The OUTPUT routine actually transfers two 512-byte blocks at a time; the INPUT routine actually transfers four 512-byte blocks at once (see Section 11.4.2).
    In general, the larger the transfers, the more efficiently the I/O is performed. The maximum I/O transfer size allowed by RMS is 65535 bytes.
  2. The error processing in this example routine is very crude; the program simply stops with an indicator of where the problem occurred. In real programs, you should provide more extensive error processing and reporting functions.
  3. The intrinsic function FOR$RAB is used to supply the appropriate RAB address to the OUTPUT and INPUT routines. The %VAL function is used to transform the address returned by the FOR$RAB intrinsic function to the proper argument passing mechanism. This allows the dummy argument RAB in INPUT and OUTPUT to be addressed properly.

11.4.2 Block Mode I/O USEROPEN Functions---BIOCREATE and BIOREAD

The only condition required for block I/O is the setting of the BIO bit in the File Access field of the FAB, using the normal declarations needed to define the symbols properly. If you wish to perform both block and record I/O on the file without closing it, you need to set the BRO bit as well. For more information on mixing block and record mode I/O, see the OpenVMS Record Management Services Reference Manual. Note that the only difference between BIOCREATE and BIOREAD is the use of SYS$CREATE and SYS$OPEN services, respectively.


! Procedure name: BIOCREATE 
 
! USEROPEN routine to set the Block I/O bit and create the BLOCK I/O file. 
 
  INTEGER FUNCTION BIOCREATE(FAB, RAB, LUN) 
  INTEGER LUN 
 
!       Declare the necessary interface names 
 
  INCLUDE '($FABDEF)' 
  INCLUDE '($RABDEF)' 
  INCLUDE '($SYSSRVNAM)' 
 
!       Declare the FAB and RAB blocks 
 
  RECORD /FABDEF/ FAB, /RABDEF/ RAB 
 
! Set the Block I/O bit in the FAC (GET and PUT bits set by RTL) 
 
      FAB.FAB$B_FAC = FAB.FAB$B_FAC .OR. FAB$M_BIO 
 
! Now do the Create and Connect 
 
  BIOCREATE = SYS$CREATE(FAB) 
  IF (.NOT. BIOCREATE) RETURN 
  BIOCREATE = SYS$CONNECT(RAB) 
  IF (.NOT. BIOCREATE) RETURN 
 
! Nothing more to do at this point, just return 
 
  RETURN 
  END FUNCTION BIOCREATE 
 
 
! Procedure name: BIOREAD 
 
! USEROPEN routine to set the Block I/O bit and open the Block I/O demo 
! file for reading 
 
  INTEGER FUNCTION BIOREAD(FAB, RAB, LUN) 
  INTEGER LUN 
 
! Declare the necessary interface names 
 
  INCLUDE '($FABDEF)' 
  INCLUDE '($RABDEF)' 
  INCLUDE '($SYSSRVNAM)' 
 
! Declare the FAB and RAB blocks 
 
      RECORD /FABDEF/ FAB, /RABDEF/ RAB 
 
! Set the Block I/O bit in the FAC (GET and PUT bits set by RTL) 
 
      FAB.FAB$B_FAC = FAB.FAB$B_FAC .OR. FAB$M_BIO 
 
! Now do the Open and Connect 
 
  BIOREAD = SYS$OPEN(FAB) 
  IF (.NOT. BIOREAD) RETURN 
  BIOREAD = SYS$CONNECT(RAB) 
  IF (.NOT. BIOREAD) RETURN 
 
!       Nothing more to do at this point, just return 
 
  RETURN 
  END FUNCTION BIOREAD 

11.4.2.1 OUTPUT Routine

The following routine initializes the array A and performs the SYS$WRITE operations. Beyond the normal RTL initialization, only the RSZ and RBF fields in the RAB need to be initialized in order to perform the SYS$WRITE operations. The %LOC function is used to create the address value required in the RBF field.

One of the main reasons that block mode I/O is so efficient is that it avoids copy operations by using the data areas of the program directly for the output buffer. When writing to a disk device, the program must specify a value for RSZ that is a multiple of 512 or else the final block would be only partly filled.


! Procedure name: OUTPUT 
 
! Function to output records in block I/O mode 
 
  LOGICAL FUNCTION OUTPUT(RAB) 
 
!       Declare RMS names 
 
  INCLUDE '($RABDEF)' 
  INCLUDE '($SYSSRVNAM)' 
 
!       Declare the RAB 
 
  RECORD /RABDEF/ RAB 
 
! Declare the Array to output 
 
  REAL(KIND=8) A(6400) 
 
!       Declare the status variable 
 
  INTEGER(KIND=4) STATUS 
 
!       Initialize the array 
 
  DO I=6400,1,-1 
    A(I) = I 
  ENDDO 
 
! Now, output the array, two 512-byte (64 elements) blocks at a time 
 
  OUTPUT = .FALSE. 
  RAB.RAB$W_RSZ = 1024 
  DO I=0,99,2 
 
! For each block, set the buffer address to the proper array element 
 
    RAB.RAB$L_RBF = %LOC(A(I*64+1)) 
    STATUS = SYS$WRITE(RAB) 
    IF (.NOT. STATUS) RETURN 
  ENDDO 
 
!       Successful output completion 
 
  OUTPUT = .TRUE. 
  RETURN 
  END FUNCTION OUTPUT 

11.4.2.2 INPUT Routine

The following routine reads the array A from the file and verifies its values. The USZ and UBF fields of the RAB are the only fields that need to be initialized. The I/O transfer size is twice as large as the OUTPUT routine. This can be done because the OUTPUT routine writes an integral number of 512-byte blocks to a disk device. This method cannot be used if the writing routine either specifies an RSZ that is not a multiple of 512 or attempts to write to a magnetic tape device.


! Procedure name: INPUT 
! 
! Function to input records in block I/O mode 
 
  LOGICAL FUNCTION INPUT(RAB) 
 
! Declare RMS names 
 
  INCLUDE '($RABDEF)' 
  INCLUDE '($SYSSRVNAM)' 
 
! Declare the RAB 
 
  RECORD /RABDEF/ RAB 
  
! Declare the Array to output 
 
  REAL(KIND=8) A(6400) 
  
!       Declare the status variable 
 
  INTEGER(KIND=4) STATUS 
 
! Now, read the array, four 512-byte (64 elements) blocks at a time 
 
  INPUT = .FALSE. 
  RAB.RAB$W_USZ = 2048 
  DO I=0,99,4 
 
! For each block, set the buffer address to the proper array element 
 
    RAB.RAB$L_UBF = %LOC(A(I*64+1)) 
    STATUS = SYS$READ(RAB) 
    IF (.NOT. STATUS) RETURN 
  ENDDO 
 
!       Successful input completion if data is correct 
 
  DO I=6400,1,-1 
     IF (A(I) .NE. I) RETURN 
  ENDDO 
 
  INPUT = .TRUE. 
  RETURN 
  END FUNCTION INPUT 


Previous Next Contents Index