HP OpenVMS Systems Documentation

Content starts here

User Manual

Previous Contents Index

13.4 I/O and Record Buffers

An I/O buffer is a storage area in your program that RMS uses to store data for I/O operations. You do not have direct access to I/O buffers; they are controlled entirely by RMS. The I/O buffer holds blocks of data transferred from the device, and its size is always greater than or equal to that of the record buffer. For more information about the amount of storage allocated for I/O buffers, see the OpenVMS Record Management Services Reference Manual.

A record buffer is another storage area in your program. You have direct access to and control of the record buffer. When your program reads a record from a file, the information is transferred from the file to the I/O buffer in one large chunk of data, and then the requested record is transferred to the record buffer. When your program writes a record, data is transferred from the record buffer to the I/O buffer, and then to the file either when the I/O buffer is full or when other blocks need to be read in.

You can use MAP statements to create static record buffers and associate program variables with areas (fields) of the buffer. Static record buffers are buffers whose size does not change during program execution and whose program variables are always associated with the same fields in the buffer.

You can create dynamic record buffers with either a MAP DYNAMIC or a REMAP statement. These statements, when used after a MAP statement, associate or reassociate a particular program variable with a different area (field) of the record buffer; however, the total size of a record buffer does not change during program execution.


If you do not specify a map, you must use MOVE TO and MOVE FROM statements to transfer data back and forth from the record buffer to program variables; however, MOVE statements do not transfer data to or from a file.

13.5 Accessing the Contents of a Record

HP BASIC provides the following methods for accessing the contents of a record:

  • MAP statement
  • MAP DYNAMIC and REMAP statements (dynamic mapping)

  • MOVE statements
  • FIELD statements

The FIELD statement is a declining feature and is not recommended for new program development. It is recommended that you use either MAP statements, dynamic mapping, or MOVE statements to access record contents.

13.5.1 MAP Statement

Normally, a record is divided into predetermined fields, the sizes of which are known at compile time. The MAP statement creates the storage area for this record and determines its total size. The following examples show how the MAP statement creates the record storage area:

Example 1

RECORD name_addr
  STRING last_name = 15,    &
         street_name = 30,  &
  INTEGER house_num
MAP (student_buffer) name_addr student_info

Example 2

MAP (Emp_rec)
    STRING Emp_name = 25,                    &
    LONG Badge,                              &
    STRING Address = 25,                     &
    STRING Department = 4

13.5.2 MAP DYNAMIC and REMAP Statements

There are situations where predetermined fields are not applicable or possible. In these situations, you must perform record defielding in your program at run time. Using the MAP DYNAMIC statement, you can specify the variables in the map whose positions can change at run time. The REMAP statement then specifies the new positions of the variables named in the MAP DYNAMIC statement.

The following example shows how you can use MAP, MAP DYNAMIC, and REMAP to deblock your record fields. The MAP statement allocates a storage area of 2048 bytes and names it Emp_rec. The MAP DYNAMIC statement specifies that the variables Emp_name, Badge, Address, and Department are all located in Emp_rec, and that their positions can be changed at run time with the REMAP statement. The REMAP statement then redefines these variables to their appropriate sizes.

MAP (Emp_rec) FILL$ = 2048

MAP DYNAMIC (Emp_rec)                                    &
    STRING Emp_name,                                     &
    LONG Badge,                                          &
    STRING Address,                                      &
    STRING Department

REMAP (Emp_rec) FILL$ = Record_offset,                   &
    Emp_name = 25,                                       &
    Badge,                                               &
    Address = 25,                                        &
    Department = 4

Note that when accessing virtual or sequential files, you can specify a RECORD clause for the GET statement. The following example opens a virtual file with each block containing 512 bytes. However, each block contains 4 logical records that are 128 bytes long. Each of these logical records consists of a 20-character first name field, a 44-character last name field, and a 64-character company name field.

DECLARE WORD Record_number
MAP (Virt) STRING FILL = 512
MAP DYNAMIC (Virt) STRING First_name,        &
                  Last_name,                 &
             VIRTUAL, MAP Virt
Record_number = 1%
  WHILE -1%
     GET #5, RECORD Record_number
     FOR I% = 0% TO 3%
        REMAP (Virt) STRING FILL = (I% * 128%),  &
              First_name = 20,                   &
              Last_name = 44,                    &
              Company = 64
        PRINT First_name, Last_name, Company
     NEXT I%
     Record_number = Record_number + 1%
   IF ERR = 11%
            PRINT "Finished"
            CONTINUE 32767
         END IF

After the first 512-byte block is brought into memory, the FOR...NEXT loop deblocks the data into 128-byte logical records. At each iteration of the FOR...NEXT loop, the REMAP statement uses the loop variable to mask off 128-byte sections of the block.

For more information about the MAP DYNAMIC and REMAP statements, see Chapter 7 and the HP BASIC for OpenVMS Reference Manual.

13.5.3 MOVE Statement

The MOVE statement defines data fields and moves them to and from the record buffer created by HP BASIC. For example:

MOVE FROM #9%, A$, Cost, Name$ = 30%, ID_num%

This statement moves a record with four data fields from the record buffer to the variables in the list as follows:

  • A string field A$ with a default length of 16 characters
  • A number field Cost of the default data type
  • A second 30-character string field Name$
  • An integer field ID_num%

Valid variables in the MOVE list are:

  • Scalar variables
  • Arrays
  • Array elements
  • FILL items

Because BASIC dynamically assigns space for string variables, the default string length during a MOVE TO operation is the length of the string. The default for MOVE FROM is 16 characters. An entire array specified in a MOVE statement must include the array name, followed by n -- 1 commas, where n is the number of dimensions in the array. Note that these commas must be enclosed in parentheses. You specify a single array element by naming the array and the subscripts of that element. The following statement moves three arrays from the program to the record buffer. A$ specifies a 1-dimensional string array, C specifies a 2-dimensional array of the default data type, and D% specifies a 3-dimensional integer array. B(3,2) specifies the element of array B that appears in row 3, column 2.

MOVE TO #5%, A$(), C(,), D%(,,), B(3,2)

Successive MOVE statements to or from the buffer start at the beginning of the record buffer. If a MOVE TO operation only partially fills the buffer, the rest of the buffer is unchanged. You use the GET statement to read a record from a file, and then you move the data from the buffer to variables and reference the variables in your program. A MOVE TO operation moves data from the variables into the buffer created by HP BASIC. A PUT or UPDATE statement then moves the data from the buffer to the file.

The following program opens file MOV.DAT, reads the first record into the buffer, and moves the data from the buffer into the variables specified in the MOVE FROM statement:

DECLARE STRING Emp_name, Address, Department

OPEN "MOV.DAT" AS FILE #1%,            &
     RELATIVE VARIABLE,                &
     RECORDSIZE 512%
GET #1%
MOVE FROM #1%,                         &
     Emp_name = 25,                    &
     Badge,                            &
     Address = 25,                     &
     Department = 4

MOVE TO #1%,                            &
     Emp_name = 25,                     &
     Badge,                             &
     Address = 25,                      &
     Department = 4


The MOVE TO statement moves the data from the named variables into the buffer. The UPDATE statement writes the record back into file MOV.DAT. The CLOSE statement closes the file.

13.6 File and Record Operations

You can perform a variety of operations on files and on the records within a file. The following is a list of all the file and record operations supported by BASIC:

  • Open a file for processing with the OPEN statement.
  • Locate a record in a file with the FIND statement.
  • Read a record from a file with the GET statement.
  • Write a record to a file with the PUT statement.
  • Delete a record from a file with the DELETE statement.
  • Change the contents of a record field with the UPDATE statement.
  • Unlock the last record accessed with the UNLOCK statement.
  • Unlock all previously locked records with the FREE statement.
  • Write data to a terminal-format file with the PRINT # statement.
  • Reset the current record pointer to the beginning of a file with the RESTORE # and RESET # statements.
  • Delete all the records after a certain point; that is, truncate the records, with the SCRATCH statement.
  • Rename a file with the NAME AS statement.
  • Close an open file with the CLOSE statement.
  • Delete an entire file with the KILL statement.

Note that before you can perform any operations on the records in a file, you must first open the file for processing.

13.6.1 Opening Files

The OPEN statement opens a file for processing, specifies the characteristics of the file to RMS, and verifies the result. Opening a file with the specification FOR INPUT specifies that you want to use an existing file. Opening a file with the specification FOR OUTPUT indicates that you want to create a new file. If you do not specify FOR INPUT or FOR OUTPUT, BASIC tries to open an existing file. If no such file exists, a new file is created.

Clauses to the OPEN statement allow you to specify the characteristics of a file. All OPEN statement clauses concerning file or record format are optional when you open an existing file; those attributes that are not specified default to the attributes of the existing file. When you open an existing file, you must specify the file name, channel number, and unless the file is a terminal-format file, an organization clause. If you do not know the organization of the file you want to open, you can specify ORGANIZATION UNDEFINED. If you specify ORGANIZATION UNDEFINED, also specify RECORDTYPE ANY.

If you do not specify a map in the OPEN statement, the size of your program's record buffer is determined by the OPEN statement RECORDSIZE clause, or by the record size associated with the file. If you specify both a MAP clause and a RECORDSIZE clause in the OPEN statement, the specified record size overrides the size specified by the MAP clause.

The following statement opens a new sequential file of stream format records:


The following example creates a relative file and associates it with a static record buffer. The MAP statement defines the record buffer's total size and the data types of its variables. When the program is compiled, BASIC allocates space in the record buffer for one integer, one 16-byte string, and one double-precision, floating-point number. The record size is the total of these fields, or 28 bytes. All subsequent record operations use this static buffer for I/O to the file.

MAP (Inv_item) LONG Part_number,                        &
           STRING Inv_name = 16,                        &
           DOUBLE Unit_price
           ,ALLOW READ, MAP Inv_item

The following OPEN statement opens a sequential file for reading only (ACCESS READ). Because the OPEN statement does not contain a MAP clause, a record buffer is created. This record buffer is 100 bytes long.

OPEN "CASE.DAT" AS FILE #1%                    &
          ,ACCESS READ                         &
          ,RECORDSIZE 100%

When you do not specify a MAP statement, your program must use MOVE TO and MOVE FROM statements to move data between the record buffer and a list of variables.

The OPEN statement for indexed files must have a MAP clause. Moreover, if you are creating an indexed file, a PRIMARY KEY clause is required. You can create a segmented index key containing more than one string variable by separating the variables with commas and enclosing them in parentheses. All the string variables must be part of the associated map.

In the following example, the primary key is made up of three string variables. This key causes the records to be sorted in alphabetical order according to the user's last name, first name, and middle initial.

MAP (Segkey) STRING First_name = 15, MI = 1, Last_name = 15
OPEN "NAMES.IND" FOR OUTPUT AS FILE #1%,                  &
       ORGANIZATION INDEXED,                              &
       PRIMARY KEY (Last_name, First_name, MI),           &
       MAP Segkey

Note that there are restrictions on the maximum record size allowed for various file and record formats. See the OpenVMS Record Management Services Reference Manual for more information.

You can use logical names to assign a mnemonic name to all or part of a complete file specification, including node, device, and directory. The advantage in using logical names is that programs do not depend on literal file specifications. You can define logical names from the following:

  • From DCL command level with the ASSIGN or DEFINE command
  • From within a program with the SYS$CRELMN system service

BASIC supports any valid logical name as part of a file specification.

A logical name specifies a 1- to 255-character name to be associated with the specified device or file specification. If the logical name specifies a device, you must end the logical name with a colon. The following example defines a logical name for a file specification:


This example defines a logical name for a physical device:


Once you define the logical name, you can reference that name in your program. For example:


These OPEN statements do not depend on the availability of DUA1: or DUA2: in order to work. If these devices are not available, you can redefine the logical names so that they specify other disk drives before running the program. In addition, you can redirect the entire file specification for PAYROLL_DATA to point to the test or production version of the data.

13.6.2 Creating Virtual Array Files

BASIC virtual arrays let you define arrays that reside on disk. You use them just as you would an ordinary array. You create a virtual array by dimensioning an array with the DIM # statement, then opening a VIRTUAL file on that channel. You access virtual arrays just as you do normal arrays.

The following DIM # statement dimensions a virtual array on channel #1. The OPEN statement opens a virtual file that contains the array. The last program line assigns a value to one array element.

DIM #1%, LONG Int_array(10,10,10)
Int_array(5,5,5) = 100%

Note that you cannot redimension virtual arrays with an executable DIM statement. See Chapter 6 for more information about virtual arrays.

13.6.3 Locating Records

The FIND statement locates a specified record and makes it the current record. Using the FIND statement to locate records can be faster than using a GET statement because the FIND statement does not transfer any data to the record buffer; therefore, it executes faster than a GET statement. However, if you are interested in the contents of a record, you must retrieve it with a GET operation.

The FIND statement sets the current record pointer to the record just found, making it the target for a GET, UPDATE, or DELETE statement. (Note that you must have write access to a record before issuing a PUT, UPDATE, or DELETE operation.) A sequential FIND operation searches records in the following order:

  • Sequential files from beginning to end
  • Relative files in ascending relative record or cell number order
  • Indexed files in ascending or descending order, based on the current key of reference and the key's collating sequence

For sequential fixed-length and relative files, you can find a particular record by specifying a RECORD clause. This is called a random access FIND. You can also perform a random access FIND for indexed files by specifying a key of reference, a relational test, and a key value.

In the following example, the first FIND statement finds the first record whose key value either equals or follows SMITH in the key's collating sequence. The second FIND statement finds the first record whose key value follows JONES in the key's collating sequence. Each record found by the FIND statement becomes the current record. (Note that you can only have one current record at a time.)

MAP (Emp) STRING Emp_name, LONG Emp_number, SSN
OPEN "EMP.DAT" AS FILE #1%, INDEXED,                &
         ACCESS READ,                               &
         MAP Emp,                                   &
         PRIMARY KEY Emp_name
FIND #1%, KEY #0% NX "JONES"

The string expression can contain fewer characters than the key of the record you want to find. However, if you want to locate a record whose string key field exactly matches the string expression you provide, you must pad the string expression with spaces to the exact length of the key of reference. For example:

FIND #5%, KEY #0% EQ "TOM     "
FIND #5%, KEY #0% EQ "TOM"

The first FIND statement locates a record whose primary key field equals "TOM ". The second FIND statement locates the first record whose primary key field begins with "TOM".

Table 13-1 displays the status of the current record and next record pointers after both a sequential and a random access FIND.

Table 13-1 Record Context After a FIND Operation
Record Access
Next Record
Sequential FIND Sequential Record found Current record + 1
  Relative Record found Next existing record
  Indexed Record found Next record in current
key order
Random access FIND All Record found Unchanged

Note that a random access FIND operation locates the specified record and makes it the current record, but the next record pointer does not change.

You can specify an ALLOW clause to the FIND statement if you have opened the file with ACCESS MODIFY or ACCESS WRITE and have specified UNLOCK EXPLICIT. The ALLOW clause lets you control the type of lock that RMS puts on the records you access. ALLOW NONE specifies that no other users can access this record (this is the default). ALLOW READ lets other users read the record; however, they cannot perform UPDATE or DELETE operations to this record. ALLOW MODIFY specifies that other users can both read and write to this record. This means that other access streams can perform GET, DELETE, or UPDATE operations to the specified record.

You can also specify a WAIT clause to the FIND statement; this clause allows you to wait for a record to become available in the event that it is currently locked by another process. In addition, you can specify a REGARDLESS clause; this clause allows you to read a locked record. For more information about the WAIT and REGARDLESS clauses, see Section 13.6.9.

Previous Next Contents Index