HP OpenVMS Systems Documentation
HP Fortran for OpenVMS
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 HP 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.2.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 ...
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.
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:
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
11.3.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 HP Fortran RTL I/O routines as when using USEROPEN routines or the FOR$RAB function.
You should always invoke RMS services as functions, rather than calling them as subroutines (see Section 10.8.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:
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 HP 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))
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 HP Fortran RTL I/O routines call these RMS services.
You can use these options related to opening a file:
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.3.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
11.3.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 HP 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
HP 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.3.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 HP 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.3.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.3.8 Other Services
Other record processing services include:
On the RMS services, see the OpenVMS Record Management Services Reference Manual.
11.4 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 HP 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:
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.
On the FAB and RAB, see the OpenVMS Record Management Services Reference Manual.
11.4.1 Examples of USEROPEN Routines
The following OPEN statement either creates a 1000-block contiguous file or returns an error. (The default HP 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 HP Fortran using HP Fortran's record handling capability.
! 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