Previous | Contents | Index |
A descriptor is an OpenVMS data structure that
describes how to access data in memory. A descriptor can also pass
information about a paramater with that parameter. The following
sections describe the formats for fixed-length and dynamic string
descriptors.
18.4.1 Fixed-Length String Descriptor Format
A fixed-length string descriptor consists of two longwords.
The first word of the first longword contains a value equal to the string's length. The third byte contains 14 (0E hexadecimal---the OpenVMS code describing an ASCII character string). The fourth byte contains 1.
The second longword is a pointer containing the address of the string's first byte. See Figure 18-7. For more information, see the OpenVMS Calling Standard.
Figure 18-7 Fixed-Length String Descriptor Format
A dynamic string descriptor consists of two longwords.
The first word of the first longword contains a value equal to the string's length. The third byte contains 14 (0E hexadecimal---the OpenVMS code describing an ASCII character string). The fourth byte contains 2.
The second longword is a pointer containing the address of the string's first character. See Figure 18-8. For more information, see the OpenVMS Calling Standard.
Figure 18-8 Dynamic String Descriptor Format
VAX BASIC and Alpha BASIC create two different types of array
descriptor. VAX BASIC creates DSC$K_CLASS_A, a contiguous class
array descriptor. Alpha BASIC creates DSC$K_CLASS_NCA, a
noncontiguous class array descriptor. For more information, see the
OpenVMS Calling Standard.
18.6 Decimal Scalar String Descriptor (Packed Decimal String Descriptor)
A single descriptor form gives decimal size and scaling information for both scalar data and simple strings. See Figure 18-9 for more information.
Figure 18-9 Decimal Scalar String Descriptor
For packed decimal strings, the length field contains the number of 4-bit digits (not including the sign). The pointer field contains the address of the first byte in the packed decimal string. The scale field contains a signed power-of-ten multiplier to convert the internal form to the external form. For example, if the internal number is 123 and the scale field is +1, then the external number is 1230.
Part 3 describes BASIC features available on OpenVMS systems including advanced file input and output, libraries and shareable images, and error messages.
This chapter describes the more advanced I/O features available in BASIC. For more information about I/O to RMS disk files, see Chapter 14. The following topics are presented:
When you do not specify a file name in the OPEN statement, the I/O you perform is said to be device-specific. This means that read and write operations (GET and PUT statements) are performed directly to or from the device. For example:
OPEN "MTA2:" FOR OUTPUT AS FILE #1 OPEN "MTA1:PARTS.DAT" FOR INPUT AS FILE #2, SEQUENTIAL |
Because the file specification in the first line does not contain a file name, the OPEN statement opens the tape drive for device-specific I/O. The second line opens an ANSI-format tape file using RMS because a file name is part of the file specification.
The following sections describe both I/O to ANSI-format magnetic tapes
and device-specific I/O to magnetic tape, unit record, and disk devices.
19.1 RMS I/O to Magnetic Tape
BASIC supports I/O to ANSI-formatted magnetic tapes. When
performing I/O to ANSI-formatted magnetic tapes, you can read or write
to only one file on a magnetic tape at a time, and the files are not
available to other users. ANSI tape files are RMS sequential files.
19.1.1 Allocating and Mounting a Tape
You should allocate the tape unit to your process before starting file operations. For example:
$ ALLOCATE MT1: |
This command assigns tape drive MT1: to your process. You must also set the tape density and label with the MOUNT command. Optionally, you can specify a logical name to assign to the device, in this case, TAPE.
$ MOUNT/DENSITY=1600 MT1: VOL001 TAPE |
When mounting a TK50, you cannot specify a density.
If the records do not specify the size of the block (no value in HDR 2), specify the BLOCKSIZE as part of the MOUNT command. For example:
$ MOUNT/DENSITY=1600/BLOCKSIZE=128 MT1: VOL020 TAPE |
Alternatively, you can use the $MOUNT system service to mount tapes.
19.1.2 Opening a Tape File for Output
To create and open a magnetic tape file for output, use the OPEN statement. The following statement opens the file PARTS.DAT and writes 256-byte records that are blocked four to a physical tape block of 1024 bytes:
OPEN "MT1:PARTS.DAT" FOR OUTPUT AS FILE #2%, SEQUENTIAL FIXED, & RECORDSIZE 256%, BLOCKSIZE 4% |
Specifying FIXED record format creates ANSI F format records. Specifying VARIABLE creates ANSI D format records. If you do not specify a record format, the default is VARIABLE.
If you do not specify a block size, BASIC defaults to one
record per block. For small records, this can be inefficient; the tape
will contain many interrecord gaps.
19.1.3 Opening a Tape File for Input
To open an existing magnetic tape file, you also use the OPEN statement. For example, the following statement opens the file PAYROLL.DAT. If you do not specify a record size or a block size, BASIC defaults to the values in the header block. If you do not specify a record format, BASIC defaults to the format present in the header block (ANSI F or ANSI D). You must specify ACCESS READ if the tape is not write-enabled. For example:
100 OPEN "TAPE:PAYROLL.DAT" FOR INPUT AS FILE #4% ,ACCESS READ |
The NOREWIND statement positions the tape for reading and writing as follows:
For example, the following statement opens PAYROL.DAT after advancing the tape to the logical end-of-tape. If you omit NOREWIND, the file opens at the beginning of the tape, logically deleting all subsequent files.
OPEN "MT1:PAYROL.DAT" FOR OUTPUT AS FILE #1% & ,ORGANIZATION SEQUENTIAL, NOREWIND |
Note that you cannot specify REWIND; to avoid rewinding the tape, omit
the NOREWIND keyword.
19.1.5 Writing Records to a File
The PUT statement writes sequential records to the file. The following program writes a record to the file. Successive PUT operations write successive records.
OPEN "MT0:TEST.DAT" FOR OUTPUT AS FILE #2, & SEQUENTIAL FIXED, RECORDSIZE 20% B$ = "" WHILE B$ <> "NO" LINPUT "Name"; A$ MOVE TO #2, A$ = 20 PUT #2 LINPUT "Write another record"; B$ NEXT CLOSE #2 END |
Each PUT writes one record to the file. If your OPEN statement specifies a RECORDSIZE clause, the record buffer length equals RECORDSIZE or the map size. For example:
RECORDSIZE 60% |
This clause specifies a record length and a record buffer size of 60 bytes. You can specify a record length from 18 to 8192 bytes. The default is 132 bytes.
If you specify a MAP clause and no RECORDSIZE clause, then the record size is the size of the map.
If you also specify BLOCKSIZE, the size of the buffer equals the value in BLOCKSIZE multiplied by the record size. For example:
RECORDSIZE 60%, BLOCKSIZE 4% |
These clauses specify a logical record length of 60 bytes and a physical tape record size of 240 bytes (60 * 4). You specify BLOCKSIZE as an integer number of records. RMS rounds the resulting value to the next multiple of four. The total I/O buffer length cannot exceed 8192 bytes. The default is a buffer (tape block) containing one record.
To write true variable-length records, use the COUNT clause with the
PUT statement to specify the number of bytes of data written to the
file. Without COUNT, all records equal the length specified by the
RECORDSIZE clause when you opened the file.
19.1.6 Reading Records from a File
The GET statement reads one logical record into the buffer. In the following example, the first GET reads a group of four records (a total of 80 bytes) from the file on channel #5 and transfers the first 20 bytes to the record buffers. Successive GET operations read 20 byte records to the record buffer performing an I/O to the tape every 4 records.
OPEN "MT0:TEST.DAT" FOR INPUT AS FILE #5%, & ORGANIZATION SEQUENTIAL FIXED, RECORDSIZE 20%, & BLOCKSIZE 4%, ACCESS READ B$ = "" WHILE B$ <> "NO" GET #5 MOVE FROM #5, A$ = 20 PRINT A$ LINPUT "Do you want another record"; B$ NEXT CLOSE #5 END |
Magnetic tape physical records range from 18 to 8192 bytes. With RMS tapes, you can optionally specify this size in the BLOCKSIZE clause as a positive integer indicating the number of records in each block. BASIC then calculates the actual size in bytes. Thus, a fixed-length file on tape with 126 byte records can have a block size from 1 to 64, inclusive. The default is 126 bytes (one record per block).
In the following example of an OPEN statement, the RECORDSIZE clause defines the size of the records in the file as 90 bytes, and BLOCKSIZE defines the size of a block as 12 records (1080 bytes). Thus, your program contains an I/O buffer of 1080 bytes. Each physical read or write operation moves 1080 bytes of data between the tape and this buffer. Every twelfth GET or PUT operation causes a physical read or write. The next eleven GET or PUT operations only move data into or out of the I/O buffer. Specifying a block size larger than the default can reduce overhead by eliminating some physical reading and writing to the tape. In addition, specifying a large block size conserves space on the tape by reducing the number of interrecord gaps (IRGs). In the example, a block size of 12 saves time by accessing the tape only after every twelfth record operation.
OPEN "MT0:[SMITH]TEST.SEQ" FOR OUTPUT AS FILE #12% & ,ORGANIZATION SEQUENTIAL FIXED, RECORDSIZE 90% & ,BLOCKSIZE 12% |
Through RMS, BASIC controls the blocking and deblocking of records. RMS checks each PUT operation to see if the specified record fits in the tape block. If it does not, RMS fills the rest of the block with circumflexes (blanks) and starts the record in a new block. Records cannot span blocks in magnetic tape files.
When you read blocks of records, your program can issue successive GET statements until it locates the fields of the record you want. The following program finds and displays a record on the terminal. You can invoke the RECOUNT function to determine how many bytes were read in the GET operation.
MAP (XXX) NA.ME$ = 5%, address$ = 20% OPEN "MTO:FILE.DAT" FOR INPUT AS FILE #4%, & SEQUENTIAL FIXED, MAP XXX, ACCESS READ NA.ME$ = "" GET #4 UNTIL NA.ME$ - "JONES" PRINT NA.ME$; "LIVES AT "; address$ CLOSE #4 END |
With the RESTORE # statement, you can rewind the tape to the start of the currently open file. For example:
OPEN "MTO:FTF.DAT" FOR INPUT AS FILE #2%, ACCESS READ GET #2% . . . RESTORE #2% GET #2% |
You cannot rewind past the beginning of the currently open file.
19.1.9 Closing a File
The CLOSE statement ends I/O to the file. The following statement ends input and output to the file open on channel #6:
CLOSE #6% |
If you opened the file with ACCESS READ, CLOSE has no further effect. If you opened the file without specifying ACCESS READ and the tape is not write-locked (that is, if the plastic write ring is in place), BASIC does the following:
BASIC does not rewind the tape.
19.2 Device-Specific I/O
Device-specific I/O lets you perform I/O directly to a device. The
following sections describe device-specific I/O to unit record devices,
tapes, and disks.
19.2.1 Device-Specific I/O to Unit Record Devices
You perform device-specific I/O to unit record devices by using only the device name in the OPEN statement file specification. You should allocate the device at DCL command level before reading or writing to the device. For example, this command allocates a card reader:
$ ALLOCATE CR1: |
Once the device is allocated, you can read records from it. For example:
MAP (DNG) A% = 80% OPEN "CR1:" FOR INPUT AS FILE #1%, ACCESS READ, MAP DNG GET #1% |
BASIC treats the device as a file, and data is read from the
card reader as a series of fixed-length records.
19.2.2 Device-Specific I/O to Magnetic Tape Devices
When performing device-specific I/O to a tape drive, you open the
physical device and transfer data between the tape and your program.
GET and PUT statements perform read and write operations. UPDATE and
DELETE statements are invalid when you perform device-specific I/O.
19.2.2.1 Allocating and Mounting a Tape
You must allocate the tape unit to your process before starting file operations. The following command line assigns tape drive MT1: to your process:
$ ALLOCATE MT1: |
Use the DCL command MOUNT and the /FOREIGN qualifier to mount the tape. For example:
$ MOUNT/FOREIGN MT1: |
If your program needs a blocksize other than 512 bytes, or a particular tape density, specify these characteristics with the MOUNT command as well. For example:
$ MOUNT/FOREIGN/BLOCKSIZE=1024/DENSITY=1600 MT1: |
When reading a foreign tape, you must make sure the /BLOCKSIZE
qualifier has a value at least as large as the largest record on the
tape.
19.2.2.2 Opening a Tape File for Output
To create and open the magnetic tape for output, you use the OPEN statement. The following statement opens tape drive MT1: for writing. It is important to use the SEQUENTIAL VARIABLE clause unless the records are fixed. In contrast to ANSI tape processing, RMS does not write record length headers or variable-length records to foreign tapes. If you specify SEQUENTIAL VARIABLE, you should have some way to determine where records begin and end.
OPEN "MT1:" FOR OUTPUT AS FILE #1%, & ORGANIZATION SEQUENTIAL VARIABLE |
To access a tape with existing data, you also use the OPEN statement. For example, the following statement opens the tape unit MT2:.
OPEN "MT2:" AS FILE #2% |
Depending on how you access records, there are two ways to open a foreign magnetic tape. If your program uses dynamic buffering and MOVE statements, open the file with no RECORDSIZE clause. RMS will provide the correct buffer size for BASIC. Do not specify a BLOCKSIZE value or ORGANIZATION clause with the OPEN statement.
If your program uses MAP and REMAP statements, but you do not know how long the records are, specify a MAP that is as large as the value you specified for the BLOCKSIZE qualifier when mounting the tape. Do not specify a BLOCKSIZE value or ORGANIZATION clause with the OPEN statement.
When processing records, each GET operation will read one physical
record whose size is returned in RECOUNT. If you are using a map only,
the first n bytes (n is the value returned in
RECOUNT) are valid.
19.2.2.4 Writing Records to a File
The PUT statement writes records to the file in sequential order. For example:
OPEN "MT0:" FOR OUTPUT AS FILE #9%, & SEQUENTIAL VARIABLE INPUT "NAME";NA.ME$ MOVE TO #9%, NA.ME$ PUT #9% |
The last line writes the contents of the record buffer to the device. Successive PUT operations write successive records.
The default record length (and, therefore, the size of the buffer) is 132 bytes. The RECORDSIZE attribute causes BASIC to read or write records of a specified length. For example, the following statement opens tape unit MT0: and specifies records of 900 characters. You must specify an even integer larger than or equal to 18. If you specify a buffer length less than 18, BASIC signals an error. If you try to write a record longer than the buffer, BASIC signals the error "Size of record invalid" (ERR=156).
OPEN "MT0:" FOR INPUT AS FILE #1%, RECORDSIZE 900% |
To write records shorter than the buffer, include the COUNT clause with the PUT statement. The following statement writes a 56-character record to the file open on channel #6. If you do not specify COUNT, BASIC writes a full buffer. You can specify a minimum count of 18, and a maximum count equal to the buffer size. When writing records to a foreign magnetic tape, neither BASIC nor RMS prefixes the records with any count bytes.
PUT #6%, COUNT 56% |
The GET statement reads records into the buffer. The following program reads a record into the buffer, prints a string field, and rewinds the file before closing. Successive GET operations read successive records. BASIC signals the error "End of file on device" (ERR=11) if you encounter a tape mark during a GET operation. If you trap this error and continue, you can skip over any tape marks. The system variable RECOUNT is set to the number of bytes transferred after each GET operation.
OPEN "MT1:" FOR INPUT AS FILE #1%, ACCESS READ GET #1% MOVE FROM #1%, A$ = RECOUNT PRINT A$ RESTORE #1% CLOSE #1% |
Previous | Next | Contents | Index |