Chapter 18
Input/Output Systems

 18.1 FIO — Sequential file I/O
 18.2 RIO — Direct file I/O
 18.3 MAG — Magnetic tape system
  18.3.1 Device management
  18.3.2 Tape positioning
  18.3.3 Example

In ADAM programs, most data will be stored in HDS objects and handled by the Data system. However, it may occasionally be necessary to read and write files directly, a need which is met by the File system which comprises the FIO and RIO libraries. They are closely related and maintain a table of ‘descriptors’ of open files to enable automatic closedown. They also assist in the production of portable software.

The FIO routines handle sequential, formatted files to produce reports for subsequent listing. The RIO routines handle direct access, unformatted files. Each can open the other’s files. When creating formatted files, it is possible to specify whether or not the file contains printer carraige control codes.

FIO and RIO use a common table of file descriptors, so RIO file descriptors may be used by appropriate FIO routines. For example, FIO_FNAME returns the filename associated with a file descriptor generated by either library.

A lot of astronomical data is stored on magnetic tape in formats which are different from the ADAM standard. Such tapes are called ‘foreign tapes’. Some of these can be read and written by ADAM packages, such as FIGARO. However, a tape may exist in a format that cannot be read by an existing ADAM program, or a programmer may wish to write a program to process a foreign tape in his own way. To answer these needs, ADAM provides a subroutine library called MAG which enables a tape device to be controlled directly. The ‘object’ which is the parameter for this system is the ‘tape drive’. It is addressed by a tape descriptor, TD.

18.1 FIO — Sequential file I/O

A file is opened by associating it with parameter PAR using the FIO_ASSOC routine:

    CALL FIO_ASSOC(PAR, MODE, FORM, RECSZ, FD, STATUS)

This has two more parameters (FORM and RECSZ) than most ASSOC routines. The mode of opening, MODE, may be ‘READ’, ‘WRITE’, ‘UPDATE’, or ‘APPEND’. A new file is created if it is ‘WRITE’, or if it is ‘APPEND’ and the file does not exist. FORM specifies the type of formatting and the carriage control processing to be used when listing the file. RECSZ is used with the RECL keyword in the Fortran OPEN statement (used in the implementation) to define a maximum record length for the file; if RECSZ is zero, the RECL keyword is omitted from the OPEN statement. A file descriptor, FD, is used to specify the file in other FIO subroutines — it is not the same as a Fortran unit number. STATUS is the usual status value indicator.

Routine FIO_CANCL cancels the association between a parameter and a file.

Routines FIO_READ and FIO_WRITE read into, or write from, buffers containing character strings stored as formatted records. These buffers can be coded or decoded using the CHR library or Fortran internal I/O. If Fortran carriage control is specified for the file, the first character in the buffer should be set appropriately.

Routine FIO_READF reads a record from a file, but does not return the used length of the buffer. This makes reading much faster when the used length is not requried. Example:  The following example shows how FIO routines are used to write a single formatted line to a file:

          SUBROUTINE EGFIO(STATUS)  
          IMPLICIT NONE  
          INCLUDE ’SAE_PAR’  
          INCLUDE ’FIO_PAR’  
          INTEGER RECSZ  
          CHARACTER*(FIO__SZMOD) MODE  
          INTEGER FD  
          INTEGER STATUS  
    *.......................................................................  
    * Use the ’WRITE’ access mode  
          MODE = ’WRITE’  
    * Create a file and open it for writing with default maximum record size  
          CALL FIO_ASSOC(’FILE’, MODE, ’FORTRAN’, 0, FD, STATUS)  
    * Write a record (note the initial carriage control space)  
          CALL FIO_WRITE(FD, ’ LINE TO BE WRITTEN’, STATUS)  
    * Close the file and cancel the parameter  
          CALL FIO_CANCL(’FILE’, STATUS)  
    * De-activate FIO  
          CALL FIO_DEACT(STATUS)  
          END

The corresponding interface file could contain the following specifications:

    interface EGFIO  
      parameter FILE  
        type    ’FILENAME’  
        prompt  ’Name of file to be created’  
        ppath   ’current,default’  
        vpath   ’prompt’  
        default DATA_DIR:NEWFILE.DAT  
      endparameter  
    endinterface

FIO_ASSOC obtains a value for parameter FILE and uses it as the filename in an internal call to FIO_OPEN. Checks on the validity of the filename are performed by the Fortran OPEN statement called internally.

Parameter FILE is defined as type FILENAME in the interface file and not as a character string, therefore the value given for ‘default’ does not need to be enclosed in quotes. Furthermore, the name of an HDS object containing the filename cannot be given.

When the program has finished with the file, it uses FIO_CANCL to close the file and cancel the parameter. If the file parameter is required again, to open the same file for reading perhaps, FIO_CLOSE should be used instead of FIO_CANCL.

The include file ‘FIO_PAR’ defines symbolic names for various constants which may be required by programs. For example, FIO__SZMOD is used in the above example to specify the length of the access mode string. Another useful symbolic name is FIO__SZFNAM (the maximum allowed length of a filename in FIO and RIO). If you want to test for explicit status values returned from FIO routines, include the statement:

    INCLUDE ’FIO_ERR’

in the program. The status can then be tested by, for example:

    IF (STATUS.EQ.FIO__ERROR) RETURN

Fortran I/O:  Fortran I/O will be required if unformatted records are to be stored in a sequential file, or formatted records in a direct access file. To do this, you will need to obtain the Fortran unit number associated with the file descriptor by using FIO_UNIT. Also, routine FIO_SERR can be used to convert a Fortran IOSTAT value to an appropriate FIO status value. For example, consider this program fragment:
    * Open a direct access file in FORMATTED mode  
          CALL RIO_ASSOC(’TEMPFILE’, ’APPEND’, ’FORMATTED’, 10, FDN, STATUS)  
    * If OK, get the Fortran unit number  
          IF (STATUS.EQ.SAI__OK) THEN  
            CALL FIO_UNIT(FDN, UNIT, STATUS)  
    * Write 5 formatted records - Must use Fortran I/O  
            DO I = 1,5  
              WRITE(UNIT, 1000, REC=I, IOSTAT=IOSTAT) I  
    1000      FORMAT(’ RECORD’, I2)  
            END DO  
    * If OK, read the third record  
            IF (IOSTAT.EQ.0) THEN  
              READ(UNIT, 1001, REC=3, IOSTAT=IOSTAT) FORM, I  
    1001      FORMAT(A7,I2)  
            END IF  
    * If the Fortran I/O failed, convert the error number  
            IF (IOSTAT.NE.0) THEN  
              CALL FIO_SERR(IOSTAT, STATUS)  
    * Else use the record  
            ELSE  
              ...  
            END IF  
          END IF  
    * Close the file and cancel the parameter  
          CALL RIO_CANCL(’TEMPFILE’, STATUS)  
    * De-activate FIO  
          CALL FIO_DEACT(STATUS)

If it is essential to use the Fortran OPEN or CLOSE statements, use FIO_GUNIT and FIO_PUNIT to obtain and release a logical unit number which does not clash with others. Note that there would be no FIO descriptor for such files.

18.2 RIO — Direct file I/O

The RIO system is very similar to the FIO system, and most of its routines have the same call sequence. For example:

    CALL FIO_ASSOC(PAR, MODE, FORM, RECSZ, FD, STATUS)  
    CALL RIO_ASSOC(PAR, MODE, FORM, RECSZ, FD, STATUS)

There are some differences however. For example, in RIO_ASSOC, RECSZ specifies the fixed record length, while in FIO_ASSOC it specifies the maximum record length. Also, the read and write routines are different:

    CALL FIO_READ(FD,        BUF,   NCHAR, STATUS)  
    CALL RIO_READ(FD, RECNO, NCHAR, BUF,   STATUS)  
 
    CALL FIO_WRITE(FD,               BUF, STATUS)  
    CALL RIO_WRITE(FD, RECNO, NCHAR, BUF, STATUS)

The RIO routines perform direct access I/O of unformatted byte arrays and need to specify a record number, RECNO. Also, they always need to specify the number of bytes, NCHAR, in a record.

Many FIO routines can be used on RIO files. For example,

    CALL FIO_FNAME(FD, FNAME, STATUS)

will get the file name, in FNAME, for any such file.

The last example in the previous section showed some RIO routines in use.

More detailed information on FIO and RIO can be found in SUN/143, which also contains routine specifications and error codes. A classified list of routines is given in Section 21.5.1.

18.3 MAG — Magnetic tape system

The routines in the MAG library have names which begin with the prefix ‘MAG_’, followed by characters which indicate their function. In this chapter, routine names will normally be written without the prefix. Thus, routine ‘MAG_READ’ will be referred to as ‘READ’.

The functions provided by the MAG library can be classified as follows:

The magnetic tape system uses the ADAM Error Strategy. If you want to test for specific MAG status values, include the statement:

    INCLUDE ’MAG_ERR’

in your program. A specific status can then be tested by, for example:

    IF (STATUS.EQ.MAG__EOV) RETURN

Linking:  To link a program with the MAG library, include ADAM_LIB:MAGLINK/OPT in the link command, for example:
    $ ALINK program,ADAM_LIB:MAGLINK/OPT

18.3.1 Device management

Device management is concerned with the allocation of a tape drive to a program, and the mounting of tapes on this drive.

Allocation and mounting are complicated by the fact that ADAM runs programs in sub-processes under the control of its own user interface. The problem is that a tape deck is allocated to a process, and when you have several processes active it matters which one the deck is allocated to. There are three ways of doing the allocation and mounting:

The first two levels have the advantage that generic device names can be used. However, they have the disadvantage that the initial tape position may not be available to the program. If absolute tape positions are not required, there is no problem. However, if they are required, special action must be taken to obtain them. One way is to use the REW routine to position the tape at its beginning. But the preferred way is to mount the tape by running the TAPEMOUNT program from the top-level process. For example, this can be done in DCL as follows:

    $ ADAMSTART  
    $ ALLOC MT TAPE  
    %DCL-I-ALLOC, _RLSTAR$MUC0: allocated  
    $ TAPEMOUNT TAPE READ  
    %MOUNT-I-MOUNTED,  mounted on _RLSTAR$MUC0:  
    $ ICL  
    ...

The first parameter of TAPEMOUNT is the name of the tape drive, and the second parameter is the access mode (READ or WRITE). When you have finished with the tape, you may dismount it using the TAPEDISM program:

    $ TAPEDISM TAPE FALSE

The first parameter of TAPEDISM is the name of the tape drive, and the second parameter should be TRUE if tape is to be unloaded (the default), and FALSE otherwise.

18.3.2 Tape positioning

The information on a tape consists of a sequence of blocks. There is a special kind of block called a ‘tape mark’ which is used to indicate the end of a file. Two consecutive tape marks indicate ‘end-of-volume’ (EOV) which normally means that there is no further information on the tape.

The ‘position’ of a tape refers to the block which will be read next when the tape moves. This block will belong to a file, so an ‘absolute position’ can be specified by a file number (FILE) and a block number (BLOCK) within that file. When a tape is in the middle of being used it will have a ‘current position’, which is the absolute position it happens to be in at a particular moment. Every absolute position has a ‘relative position’ relative to the current position. Some MAG routines specify blocks by absolute position, and others by relative position. For example:

    CALL MAG_MOVE(TD, 2, ’TRUE’, 1, STATUS)

will position the tape at the first block of the second file and will assume it is being read in a forward direction, while:

    CALL MAG_JUMP(TD, 5, STATUS)

will skip forward 5 blocks. A tape’s absolute position can be found at any time by the call:

    CALL MAG_POS(TD, FILE, START, BLOCK, STATUS)

If the position is unknown, FILE and BLOCK will be set to zero.

When reading forwards, the first data block in a file is specified by BLOCK=1. However, when reading backwards, the last data block in a file is specified by BLOCK=2. Position memory:  The MAG package enables the position of a tape on a specific drive to be remembered between program runs. However, this can only be done if the tape is mounted using MAG_MOUNT. When a program has finished with a tape, it must call MAG_CANCL or MAG_ANNUL, at which time the tape must be in a known absolute position. MAG_MOUNT will initialize the position at FILE=1, START=.TRUE., BLOCK=1, while MAG_DISM will make the values undefined.

18.3.3 Example

The following example is a sketch of a program which reads a magnetic tape using MAG routines:

          SUBROUTINE EGMAG(STATUS)  
          IMPLICIT NONE  
          INCLUDE ’SAE_PAR’  
          INCLUDE ’MAG_PAR’  
          INTEGER TD  
          INTEGER STATUS  
    *........................................................  
    * Associate the TAPE parameter with an actual tape drive  
          CALL MAG_ASSOC(’TAPE’, ’READ’, TD, STATUS)  
    * Rewind the tape  
          CALL MAG_REW(TD, STATUS)  
    * Process the tape  
          ...  
    * Release the tape drive  
          CALL MAG_ANNUL(TD, STATUS)  
          END

The corresponding interface file could contain:

    interface EGMAG  
      parameter TAPE  
        type    ’TAPE’  
        ptype   ’DEVICE’  
        prompt  ’Tape Deck’  
        ppath   ’current,default’  
        vpath   ’prompt’  
        default MTA0  
      endparameter  
    endinterface

MAG_ASSOC requests that the associated tape drive be accessed in ‘READ’ mode, and sets a tape descriptor, TD, which is used by other MAG routines to identify the drive to be used. When the program has finished with the tape, it uses MAG_ANNUL to release the drive and store the tape position in the environment.

The error symbols and full routine specifications for the MAG library can be found in APN/1. Also, a classified list of the routines is given in Section 21.5.2.