# Compaq BASIC for OpenVMS
Alpha and VAX Systems
User Manual

Previous | Contents | Index

## 14.5.3 MOVE Statement

The MOVE statement defines data fields and moves them to and from the record buffer created by 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 as follows:

- 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 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
    DECLARE LONG Badge

    OPEN "MOV.DAT" AS FILE #1%,            &
         RELATIVE VARIABLE,                &
         ACCESS MODIFY, ALLOW NONE,        &
         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

    UPDATE #1%
    CLOSE #1%
    END

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.

# 14.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.

## 14.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:

    OPEN "TEST.DAT" FOR OUTPUT AS FILE #1%,        &
          SEQUENTIAL STREAM

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
    OPEN "INVENTORY.DAT" FOR OUTPUT AS FILE #1%             &
               ,ORGANIZATION RELATIVE FIXED, ACCESS MODIFY  &
               ,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%                    &
              ,ORGANIZATION SEQUENTIAL VARIABLE    &
              ,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
- From within the VAX BASIC Environment with the BASIC command ASSIGN

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:

    $ ASSIGN DUA1:[SENDER]PAYROL.DAT PAYROLL_DATA

This example defines a logical name for a physical device:

    $ ASSIGN DUA2: DISK2:

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

    OPEN "PAYROLL_DATA" FOR INPUT AS FILE #1%,  &
             ORGANIZATION SEQUENTIAL
    OPEN "DISK2:[SORT_DATA] SORT.LIS" FOR OUTPUT AS FILE #2%, &
             SEQUENTIAL VARIABLE

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.

## 14.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)
       .
       .
       .
    OPEN "VIRT.DAT" FOR OUTPUT AS FILE #1%, VIRTUAL
       .
       .
       .
    Int_array(5,5,5) = 100%

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

## 14.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% NXEQ "SMITH"
    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 14-1 displays the status of the current record and next record pointers after both a sequential and a random access FIND.

Table 14-1 Record Context After a FIND Operation
Record Access Mode | File Type | Current Record | 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 14.6.9.

## 14.6.4 Reading Records

The GET statement moves a record from a file to a record buffer and makes the data available for processing. GET statements are valid on sequential, relative, and indexed files. You should not use GET statements on terminal-format files or virtual array files.

For sequential files, a sequential GET retrieves the next record in the file. For relative files, a sequential GET retrieves the next existing record. For indexed files, a sequential GET retrieves the record with the next ascending or descending value in the current key of reference, depending on that key's collating sequence.

Table 14-2 shows the current record and next record pointers after a GET operation. Note that the values of these pointers vary, depending on whether or not the previous operation was a FIND.

Table 14-2 Record Context After a GET Operation
Record Access Mode | File Type | Current Record | Next Record
--- | --- | --- | ---
Sequential GET with FIND | Sequential | Record found | Current record + 1
| Relative | Record found | Next existing record
| Indexed | Record found | Next record in current key
Sequential GET without FIND | Sequential | Next record | Next record + 1
| Relative | Next existing record | Next existing record + 1
| Indexed | Next record in current key | Record following next record in current key
Random GET | All | Record specified | Next record in succession

If you precede a sequential GET operation with a FIND operation, the current record is the one located by FIND. If you do not perform a FIND operation before a sequential GET operation, the current record is the next sequential record.

The following example shows the use of the GET operation to sequentially access records in an indexed file. The example opens an indexed file and displays the first 25 records with serial numbers greater than AB2721 in ascending primary key value order.

    MAP (Bec) STRING Owner = 30%, LONG Vehicle_number,     &
              STRING Serial_number = 22%
    OPEN "VEH.IDN" FOR INPUT AS FILE #2%,                  &
          ORGANIZATION INDEXED, PRIMARY KEY Serial_number, &
          MAP Bec, ACCESS READ
    GET #2%, KEY #0% EQ "AB2721"
    FOR I% = 1% TO 25%
          GET #2%
          PRINT "Vehicle Number = ";Vehicle_number
          PRINT "Owner is: ";Owner
          PRINT
    NEXT I%

The following example performs random GET operations by specifying a record number:

    MAP (Bec) STRING Owner = 30%, LONG Vehicle_number,    &
              STRING Serial_number = 22%
    OPEN "VEH.IDN" FOR INPUT AS FILE #2%,                 &
          ORGANIZATION SEQUENTIAL FIXED,                  &
          MAP Bec, ACCESS READ
    INPUT "Which record do you want";A%
    WHILE (A% <> 0%)
       GET #2%, RECORD A%
       PRINT "The vehicle number is", Vehicle_number
       PRINT "The serial number is", Serial_number
       PRINT "The owner of vehicle";Vehicle_number; "is", Owner
       INPUT "Next Record";A%
    NEXT
    CLOSE #2%
    END

You can specify an ALLOW clause in a GET statement if you have opened the file with ACCESS MODIFY or ACCESS WRITE and UNLOCK EXPLICIT. The ALLOW clause lets you control the type of lock RMS places on the retrieved record. ALLOW NONE specifies that no other users can access this record (this is the default). ALLOW READ lets other access streams have read access to the record. That is, other users can retrieve the record, but cannot perform DELETE, PUT, or UPDATE operations on it. ALLOW MODIFY lets other access streams perform GET, DELETE, or UPDATE operations on the record.

If you are trying to access a locked record, BASIC signals "Record/bucket locked" (ERR=154). However, if you only need to read this record, you can override the lock with the REGARDLESS clause. The REGARDLESS clause allows you to read a locked record. Use caution when using the REGARDLESS clause because a record accessed in this way might be in the process of being changed by another program.

Alternatively, you can also specify the WAIT clause on a GET statement; the WAIT clause allows you to handle record locked conditions by waiting for the record to become available. Note that if a WAIT clause is specified on a GET operation to a unit-record device such as a terminal, the integer expression indicates how long to wait for the I/O to complete, rather than how long to wait on a record locked condition. For more information, see Section 14.6.9.

## 14.6.5 Writing Records

For a file opened with ACCESS WRITE or ACCESS MODIFY, the PUT statement moves data from the record buffer to a file using the I/O buffer. PUT statements are valid on RMS sequential, relative, and indexed files. You cannot use PUT statements on terminal-format files or virtual array files.

Sequential access is valid on RMS sequential, relative, and indexed files. For sequential, variable, and stream files, a sequential PUT operation adds a record at the end of the file. For sequential fixed and relative files, PUT writes records sequentially or randomly depending on the presence of a RECORD clause. For indexed files, RMS stores records in order of the primary key's collating sequence; therefore, you do not need to specify a random or sequential PUT. Table 14-3 shows the record context after both random and sequential PUT operations.

Table 14-3 Record Context After a PUT Operation
Record Access Mode | File Type | Current Record | Next Record
--- | --- | --- | ---
Sequential PUT | Sequential | None | End of file
Sequential PUT | Relative | None | Next record
Sequential PUT | Indexed | None | Undefined
Random PUT | Relative | None | Unchanged

After a PUT operation, the current record pointer has no value. However, the value of the next record pointer changes depending on the file type and the record access mode used with the PUT operation. In a sequential, stream, or variable file, records can only be added at the end of the file; therefore, the next record after PUT is the end of the file. In a relative, sequential, or fixed file, the next record after a PUT operation is the next logical record.

The following example opens a sequential file with ACCESS APPEND specified. For sequential files, this is almost identical to ACCESS WRITE. The only difference is that, with ACCESS APPEND, BASIC positions the file pointer after the last record in the file when it opens the file for processing. All subsequent PUT operations append the new record to the end of the existing file.

    MAP (Buff) STRING Code = 4%, Exp_date = 9%, Type_desig = 32%
    OPEN "INV.DAT"FOR INPUT AS FILE #2%,                  &
          ORGANIZATION SEQUENTIAL FIXED, ACCESS APPEND,   &
          MAP Buff
    WHILE -1%
          INPUT "What is the specification code";Code
          INPUT "What is the expiration date";Exp_date
          INPUT "What is the designator";Type_desig
          PUT #2%
    NEXT

If the current record pointer is not at the end of the file when you attempt a sequential PUT operation to a sequential file, BASIC signals "Not at end of file" (ERR=149).

In the following example, the PUT statement writes records to an indexed file. In this case, the error message "Duplicate key detected" (ERR=134) indicates that a record with a matching key field already exists, and you did not allow duplicates on that key.

    10      MAP (Purchase_rec) STRING R_num = 5,              &
                                      Dept_name = 10,         &
                                      Pur_dat = 9
    20      OPEN "INFO.DAT"FOR OUTPUT AS FILE #2,               &
                    ORGANIZATION INDEXED FIXED, ACCESS WRITE, &
                    PRIMARY KEY R_num, MAP Purchase_rec
    30      WHILE -1%
                    INPUT "Requisition number";R_num
                    INPUT "Department name";Dept_name
                    INPUT "Date of purchase";Pur_dat
                    PRINT
                    PUT #2%
            NEXT

    Requisition number? 2522A
    Department name? COSMETICS
    Date of purchase? 15-JUNE-1985

    Requisition number? 2678D
    Department name? AUTOMOTIVE
    Date of purchase? 15-JUNE-1985

    Requisition number? 4167C
    Department name? AUTOMOTIVE
    Date of purchase? 6-JANUARY-1985

    Requisition number? 2522A
    Department name? SPORTING GOODS
    Date of purchase? 25-FEBRUARY-1985

    %BAS-F-DUPKEYDET, Duplicate key detected
    -BAS-I-ON_CHAFIL, on channel 2 for file USER$$DISK:[MAGNUS]INFO.DAT;8 at
    user PC 0017E593
    -BAS-O-FROLINMOD, from line 30 in module DUPLICATES
    -RMS-F-DUP, duplicate key detected (DUP not set)

Previous | Next | Contents | Index