Date: Tue, 20 Jan 87 19:13:15 est
From: David Krowitz <mit-erl!mit-kermit!piggy!krowitz@EDDIE.MIT.EDU>

Subject: new version of SENDREC/GETREC package

Below the dashed line is the first part of a new version
of the SENDREC file conversion utility that I sent you
previously. The second part is in the next message. This
version (version 6) has been tested with both an Alliant
FX/1 (and Apollo DSP9000) and a Sun-3/160 workstation
and should replace the earlier version I sent you.

                            -- David Krowitz
--------------------------------------------------------------
# To unbundle, sh this file
echo getrec.bld 1>&2
cat >getrec.bld <<'End of getrec.bld'
von
pas getrec.pas
bind -b getrec getrec.bin
voff
End of getrec.bld
echo getrec.hlp 1>&2
cat >getrec.hlp <<'End of getrec.hlp'
5.0;getrec (get_record), revision 5.0, 86/08/12
GETREC (GET_RECORD) -- Convert a record-structured file from DSP9000 or Sun workstation format.
usage:  GETREC [input file name] [output file name] {[-S] [-D record-length]}


FORMAT

  GETREC  [input file] [output file] [file type switch]

  GETREC reads a record-structured binary data file created by a
  DSP9000 (an Alliant FX/1 or FX/8) or a Sun workstation and
  converts the file into an Apollo REC format file which can be
  read by an Apollo node.


ARGUMENTS

  input file         Name of the DSP9000/Sun format file to be converted. If not
  (optional)         given on the command line the user will be prompted for
                     the input file name.

  output file        Name of the Apollo REC format file to be written. If not
  (optional)         given on the command line the user will be prompted for
                     the output file name.


SWITCHES


  -S                 Specifies that input file is a DSP9000/Sun sequential-access
                     format binary file.

  -D record_size     Specifies that input file is a DSP9000/Sun direct-access
                     format binary file. Record size in bytes of the DSP9000/Sun
                     direct-access records must follow the switch.




EXAMPLES

  1.  $ getrec data.fx1 data.apollo -s          Converts a sequential-access file.
                .
                .
                .

  2.  $ getrec data.fx1 data.apollo             Converts a sequential-access file.
        Enter a D to process a direct-access file from the Alliant/Sun,
        Enter an S to process a sequential-access file from the Alliant/Sun: S
                .
                .
                .

  3.  $ getrec -D 36000 data.fx1 data.apollo    Converts a direct-access file
                .                               with 36000 byte records.
                .
                .

  4.  $ getrec -D 36000 data.fx1                Converts a direct-access file.
        Enter name of AEGIS format file for output: data.apollo
                .
                .
                .

End of getrec.hlp
echo getrec.pas 1>&2
cat >getrec.pas <<'End of getrec.pas'
{*****************************************************************************
 *****                                                                   *****
 *****                             GETREC.PAS                            *****
 *****                                                                   *****
 *****      Program to read a binary data file created by an unformatted *****
 *****      Fortran write on an Alliant FX/1 or FX/8 (a BSD4.2 Unix      *****
 *****      machine) or a Sun workstation which have been put into an    *****
 *****      AEGIS ascii file (UASC file) by the ethernet File Transfer   *****
 *****      Program (FTP) or which is being read via a transparent file  *****
 *****      access scheme (NFS or the V2 demo) and which converts the    *****
 *****      file into an AEGIS format binary data file (REC file) which  *****
 *****      can be read by an unformatted Fortran read on an Apollo      *****
 *****      machine running AEGIS.                                       *****   
 *****                                                                   *****
 *****                            Version 6                              *****
 *****                  David M. Krowitz January 20, 1987.               *****
 *****                                                                   *****
 *****      Copyright (c) 1986                                           *****
 *****      David M. Krowitz                                             *****
 *****      Massachusetts Institute of Technology                        *****
 *****      Department of Earth, Atmospheric, and Planetary Sciences     *****
 *****************************************************************************
}


PROGRAM GETREC;


%NOLIST;
%INSERT '/sys/ins/base.ins.pas';
%INSERT '/sys/ins/streams.ins.pas';
%INSERT '/sys/ins/pgm.ins.pas';
%LIST;




CONST

{Program version number - should be same as in file header above}

    version_number = 6;


{Maximum number of bytes requested to be read into memory from
 the unformatted Alliant or Sun Fortran binary data (UASC)file by
 the STREAM_$GET_BUF routine.}

    maxsize             = 256*1024;                     {Request 256K bytes at a time}

{Maximum number of bytes in a file name}

    maxname             = 80;


TYPE

    file_name_t = packed array[1..maxname] of char;     {Input and output file name types}
    count_ptr_t = ^linteger;                            {Pointer to a record count}
    record_ptr_t = ^record_t;                           {Pointer to the data in a record}
    record_t = packed array[1..maxsize+4] of char;      {Space for max. record plus 2nd record count}

        
VAR

{Definitions of input and output files}

    asc_file_name:      file_name_t;                        {AEGIS ascii (UASC file) input file}
    rec_file_name:      file_name_t;                        {Fortran unformatted (REC file) output file}
    asc_name_length:    pinteger;                           {length of ASC_FILE_NAME}
    rec_name_length:    pinteger;                           {length of REC_FILE_NAME}



{Defintions of variables for stream I/O}

    asc_ptr:            UNIV_PTR;               {pointer to first byte returned by STREAM_$GET_BUF}
    asc_stream:         STREAM_$ID_T;           {stream id of input file}
    rec_ptr:            UNIV_PTR;               {pointer to first byte to be written by STREAM_$PUT_REC}
    rec_stream:         STREAM_$ID_T;           {stream id of output file}
    rec_attrib:         STREAM_$IR_REC_T;       {attribute block for setting AEGIS file characteristics}
    rec_err:            STREAM_$REDEF_MASK_T;   {error mask for setting AEGIS file characteristics}



{Defintions of global variables}

    i,j:                INTEGER32;                  {counters}
    arg_string:         array [1..80] of CHAR;      {Input string for getting args from command line}
    arg_len:            INTEGER16;                  {Length of ARG_STRING}
    file_type:          CHAR;                       {'S' for sequential-access files, 'D' for direct-access files}
    record_count1:      INTEGER32;                  {First copy of Alliant/Sun record count}
    record_count2:      INTEGER32;                  {Second copy of Alliant/Sun  record count}
    record_num:         INTEGER32;                  {Number of record currently being processed}
    record_len:         INTEGER32;                  {Number of bytes in the AEGIS record (without the record count)}
    record_size:        INTEGER32;                  {Length of direct-access records in bytes}
    record_ptr:         record_ptr_t;               {Pointer to record buffer}
    record_buffer:      record_t;                   {Buffer to hold data from AEGIS record (without the record count)}
    count_ptr:          count_ptr_t;                {Pointer to record count}
    data_ptr:           UNIV_PTR;                   {Pointer to the data returned by STREAM_$GET_BUF}
    status:             status_$t;                  {Status returned by stream I/O calls}
    seek_key:           STREAM_$SK_T;               {Seek key for file (not used by us)}



PROCEDURE process_sequential_file;

    {Read a record and the record count from the Alliant/Sun sequential-
     access format data file. Note that the record count that we read
     is the number of bytes of data in the record NOT INCLUDING THE
     RECORD COUNT ITSELF. The actual 32-bit counter in the AEGIS record
     counts itself as part of the record. Also note that the Alliant/Sun format
     file also contains a second copy of the record count at the end of
     the record, so that while the complete Alliant/Sun record is 4 bytes longer
     than the corresponding AEGIS record, the Alliant/Sun record count it 4 less
     than the corresponding AEGIS record count. Note that the pointer to
     the data returned by STREAM_$GET_BUF is only valid until the next
     STREAM call. STREAM_$GETxxx may not return the data in the buffer
     you supply, but may simply map a portion of the file into your
     address space and return a pointer to the data rather. The next
     STREAM call may unmap that portion of the file. Thus we must read
     the data portion of the Alliant/Sun record and the second copy of the
     record count in one operation. If we where to read them in two
     operations (data, then second count) the data could get unmapped
     by the STREAM call to get the second counter.}

    BEGIN

        record_num := 0;

        REPEAT
            record_num := record_num+1;

            {Read the complete Alliant/Sun sequential-access format record (including
             both copies of the record count) from the unformatted UASC file
             which FTP created when it transferred the file from the Alliant/Sun.}

            STREAM_$GET_BUF (asc_stream,ADDR(record_count1),4,
                             data_ptr,record_len,seek_key,status);
            IF (status.code <> STATUS_$OK) AND (status.code <> STREAM_$END_OF_FILE) THEN BEGIN
                WRITELN ('**** GETREC: Error - unable to read 1st record count of input file ****');
                WRITELN ('****                 record = ',record_num:17,'                    ****');
                PGM_$EXIT;
            END;


            IF (status.all = STATUS_$OK) THEN BEGIN
                count_ptr := data_ptr;                  {Copy the universal pointer so the count can be accessed}
                record_count1 := count_ptr^;            {Get the first copy of the record count}

                IF (record_count1 > maxsize) THEN BEGIN
                    WRITELN ('**** GETREC: Error - record count greater than input buffer size ****');
                    WRITELN ('****                 record = ',record_num:17,'                  ****');
                    WRITELN ('****                 record count = ',record_count1:20,'         ****');
                    PGM_$EXIT;
                END;

                IF (record_count1 < 0) THEN BEGIN
                    WRITELN ('**** GETREC: Error - record count less than zero -- not valid  ****');
                    WRITELN ('****                 record = ',record_num:17,'                ****');
                    WRITELN ('****                 record count = ',record_count1:20,'       ****');
                    PGM_$EXIT;
                END;

                IF ODD(record_count1) THEN BEGIN
                    WRITELN ('**** GETREC: Error - unable to convert record, odd number of bytes ****');
                    WRITELN ('****                 in record = ',record_num:17,'                 ****');
                    WRITELN ('****                 # bytes   = ',record_count1:20,'              ****');
                    PGM_$EXIT;
                END;
    

                STREAM_$GET_BUF (asc_stream,ADDR(record_buffer),record_count1+4,
                                 data_ptr,record_len,seek_key,status);
                IF (status.code <> STATUS_$OK) THEN BEGIN
                    WRITELN ('**** GETREC: Error - unable to read data portion of record from input file ****');
                    WRITELN ('****                 record = ',record_num:17,'                            ****');
                    WRITELN ('****                 bytes requested = ',record_count1+4:20,'              ****');
                    WRITELN ('****                 bytes read      = ',record_len:17,'                   ****');
                    PGM_$EXIT;
                END;

                IF (record_len < 0) THEN BEGIN
                    WRITELN ('**** GETREC: Error - record is too large for input buffer ****');
                    WRITELN ('****                 record = ',record_num:17,'           ****');
                    WRITELN ('****                 # bytes   = ',record_len:17,'        ****');
                    PGM_$EXIT;
                END;

                IF (record_count1 <> record_len-4) THEN BEGIN
                    WRITELN ('**** GETREC: Error - wrong amount of data read from input file ****');
                    WRITELN ('****                 record = ',record_num:17,'                ****');
                    WRITELN ('****                 bytes requested = ',record_count1:20,'    ****');
                    WRITELN ('****                 bytes read      = ',record_len-4:17,'     ****');
                    PGM_$EXIT;
                END;

                record_ptr := data_ptr;                             {Copy the universal pointer so the data can be accessed}
                count_ptr := ADDR(record_ptr^[record_count1+1]);    {Make pointer to second copy of the record count}
                record_count2 := count_ptr^;                        {Get the second copy of the record count}

                IF (record_count1 <> record_count2) THEN BEGIN
                    WRITELN ('**** GETREC: Error - 1st and 2nd record counts of input file are not equal ****');
                    WRITELN ('****                 record = ',record_num:17,'                            ****');
                    WRITELN ('****                 record count 1 = ',record_count1:20,'                 ****');
                    WRITELN ('****                 record count 2 = ',record_count2:20,'                 ****');
                    PGM_$EXIT;
                END;


                {Alliant/Sun sequential-access format record seems to be OK, go ahead
                 and copy the data portion of the record to the AEGIS output file.}

                STREAM_$PUT_REC (rec_stream,record_ptr,record_count1,seek_key,status);
                IF (status.all <> 0) THEN BEGIN
                    WRITELN ('**** GETREC: Error -  unable to write to AEGIS format output file ****');
                    WRITELN ('****                  on record = ',record_num:17,'               ****');
                    PGM_$EXIT;
                END;

                WRITELN ('');
                WRITELN ('record:     ',record_num:-1);
                WRITELN ('size:       ',record_count1:-1);

            END;

        UNTIL (status.code = STREAM_$END_OF_FILE);

    END;        {End of procedure PROCESS_SEQUENTIAL_FILE.}



PROCEDURE process_direct_file;

     {Read a record from the Alliant/Sun direct-access format file, add in
      the fixed length record size we got from the user, and write the
      corresponding Apollo record. Note that the Alliant/Sun records for
      direct-access (ie. fixed length record format) files do not have
      record counts stored in the file. Also note that the pointer to
      the data returned by STREAM_$GET_BUF is only valid until the next
      STREAM call. STREAM_$GETxxx may not return the data in the buffer
      you supply, but may simply map a portion of the file into your
      address space and return a pointer to the data rather. The next
      STREAM call may unmap that portion of the file.}

    BEGIN
                                                                    

        {Check that the record size supplied by the user for the fixed
         length records of the Alliant/Sun file is a legal record size. Then
         start reading and converting the records from the Alliant/Sun file.}

        IF (record_size > maxsize) THEN BEGIN
            WRITELN ('**** GETREC: Error - record size greater than input buffer size ****');
            WRITELN ('****                 record size = ',record_size:18,'           ****');
            PGM_$EXIT;
        END;

        IF (record_size < 0) THEN BEGIN
            WRITELN ('**** GETREC: Error - record size less than zero -- not valid ****');
            WRITELN ('****                 record size = ',record_size:18,'        ****');
            PGM_$EXIT;
        END;

        IF ODD(record_size) THEN BEGIN
            WRITELN ('**** GETREC: Error - unable to convert record, odd number of bytes ****');
            WRITELN ('****                 # bytes   = ',record_size:18,'                ****');
            PGM_$EXIT;
        END;

        record_num := 0;

        REPEAT
            record_num := record_num+1;

            {Read the complete Alliant/Sun format direct-access record (without
             any record count) from the unformatted UASC file which FTP created
             when it transferred the file from the Alliant/Sun machine.}

            STREAM_$GET_BUF (asc_stream,ADDR(record_buffer),record_size,
                             data_ptr,record_len,seek_key,status);
            IF (status.code <> STATUS_$OK) AND (status.code <> STREAM_$END_OF_FILE) THEN BEGIN
                WRITELN ('**** GETREC: Error - unable to read record from input file  ****');
                WRITELN ('****                 record = ',record_num:17,'             ****');
                PGM_$EXIT;
            END;


            IF (status.all = STATUS_$OK) THEN BEGIN

                IF (record_len < 0) THEN BEGIN
                    WRITELN ('**** GETREC: Error - record is too large for input buffer ****');
                    WRITELN ('****                 record = ',record_num:17,'           ****');
                    WRITELN ('****                 # bytes   = ',record_len:17,'        ****');
                    PGM_$EXIT;
                END;

                IF (record_size <> record_len) THEN BEGIN
                    WRITELN ('**** GETREC: Error - wrong amount of data read from input file ****');
                    WRITELN ('****                 record = ',record_num:17,'                ****');
                    WRITELN ('****                 bytes requested = ',record_size:18,'      ****');
                    WRITELN ('****                 bytes read      = ',record_len:15,'       ****');
                    PGM_$EXIT;
                END;

 
               {Alliant/Sun direct-access format record seems to be OK, go ahead
                and copy the record to the AEGIS output file. An AEGIS style
                record count will automatically be written into the record.}

                STREAM_$PUT_REC (rec_stream,data_ptr,record_size,seek_key,status);
                IF (status.all <> 0) THEN BEGIN
                    WRITELN ('**** GETREC: Error -  unable to write to AEGIS format output file ****');
                    WRITELN ('****                  on record = ',record_num:17,'               ****');
                    PGM_$EXIT;
                END;

                WRITELN ('');
                WRITELN ('record:     ',record_num:-1);
                WRITELN ('size:       ',record_size:-1);

            END;

        UNTIL (status.code = STREAM_$END_OF_FILE);

    END;        {End of procedure PROCESS_SEQUENTIAL_FILE.}

                                                           



BEGIN

    {Type initial greetings to user.}

    WRITELN ('This is GETREC Version ',version_number:-1,'.');
    WRITELN;


    {Get the arguments from the command line.
     If not all of the arguments are on the command line
     then prompt the user for the missing arguments.}

    asc_name_length := 0;
    rec_name_length := 0;
    file_type := ' ';

    arg_len := PGM_$GET_ARG(1,arg_string,status,80);
    WHILE (arg_len <> 0) DO BEGIN
        IF (arg_string[1] = '-') THEN BEGIN
            file_type := arg_string[2];
            IF NOT(file_type IN ['s','S','d','D']) OR (arg_len <> 2) THEN BEGIN
                WRITELN ('**** GETREC: Error - unknown switch ''',arg_string:arg_len,''' ****');
                PGM_$EXIT;
            END;
            PGM_$DEL_ARG (1);
            IF (file_type IN ['d','D']) THEN BEGIN
                record_size := 0;
                arg_len := PGM_$GET_ARG(1,arg_string,status,80);
                IF (arg_len = 0) THEN BEGIN
                    WRITELN ('**** GETREC: Error - no record length for -D switch ****');
                    PGM_$EXIT;
                END;
                FOR i := 1 TO arg_len DO BEGIN
                    IF (arg_string[i] IN ['0'..'9']) THEN BEGIN
                        record_size := record_size*10+ORD(arg_string[i])-ORD('0');
                    END
                    ELSE BEGIN
                        WRITELN ('**** GETREC: Error - bad record length for direct-access file ****');
                        PGM_$EXIT;
                    END;
                END;
                PGM_$DEL_ARG (1);
            END;
        END
        ELSE BEGIN
            IF (asc_name_length = 0) THEN BEGIN
                asc_name_length := PGM_$GET_ARG(1,asc_file_name,status,maxname);
                IF (status.all <> STATUS_$OK) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - 1st file name exists, but bad status? ****');
                    PGM_$EXIT;
                END;
            END
            ELSE BEGIN
                rec_name_length := PGM_$GET_ARG(1,rec_file_name,status,maxname);
                IF (status.all <> STATUS_$OK) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - 2nd file name exists, but bad status? ****');
                    PGM_$EXIT;
                END;
            END;
            PGM_$DEL_ARG (1);
        END;
        arg_len := PGM_$GET_ARG (1,arg_string,status,80);
    END;



    IF (asc_name_length = 0) THEN BEGIN
        WRITE ('Enter name of Alliant/Sun format file for input: ');
        READLN (asc_file_name);
        asc_name_length := maxname;
    END;

    IF (rec_name_length = 0) THEN BEGIN
        WRITE ('Enter name of AEGIS format file for output: ');
        READLN (rec_file_name);
        rec_name_length := maxname;
    END;

    {Open the Alliant/Sun unformatted binary data (UASC) input file.}

    STREAM_$OPEN (asc_file_name,asc_name_length,STREAM_$UPDATE,
                  STREAM_$NO_CONC_WRITE,asc_stream,status);
    IF (status.all <> 0) THEN BEGIN
        WRITELN ('**** GETREC: Error - unable to open the Alliant/Sun format binary data (UASC) input file ****');
        PGM_$EXIT;
    END;


    {Create and open the AEGIS format output file.
     First try to open the file to see if it exists. If it does, then
     delete it. STREAM_$CREATE should be able to overwrite an existing
     object, but it seems to have trouble with large files.}

    STREAM_$OPEN (rec_file_name,rec_name_length,STREAM_$UPDATE,
                  STREAM_$NO_CONC_WRITE,rec_stream,status);
    IF (status.all = STATUS_$OK) THEN BEGIN
        STREAM_$DELETE (rec_stream,status);
    END;
    STREAM_$CREATE_BIN (rec_file_name,rec_name_length,STREAM_$OVERWRITE,
                        STREAM_$NO_CONC_WRITE,rec_stream,status);
    IF (status.all <> STATUS_$OK) THEN BEGIN
        WRITELN ('**** GETREC: Error - unable to open the AEGIS format binary data (REC) output file ****');
        PGM_$EXIT;
    END;

    
    {Check if file is a sequential-access record structured file
     or if it is a direct-access record structured file. Alliant/Sun has a
     different file format for direct-access files (no record counts at
     all in direct-access files).}

    IF (file_type = ' ') THEN BEGIN
        REPEAT
            WRITELN;
            WRITELN ('Enter a D to process a direct-access file from the Alliant/Sun,');
            WRITE ('Enter an S to process a sequential-access file from the Alliant/Sun: ');
            READLN (file_type);
        UNTIL (file_type IN ['d','D','s','S']);
        IF (file_type IN ['d','D']) THEN BEGIN
            WRITELN;
            WRITE ('Enter length of direct-access record in bytes: ');
            READLN (record_size);
        END;
    END;

    IF (file_type IN ['s','S']) THEN BEGIN
        rec_attrib.explicit_type := FALSE;
        rec_attrib.rec_type := STREAM_$V1;
        STREAM_$REDEFINE (rec_stream,[STREAM_$EXPLICIT_TYPE,STREAM_$REC_TYPE],
                          rec_attrib,rec_err,status);
        IF (status.all <> STATUS_$OK) THEN BEGIN
            WRITELN ('**** GETREC: Error - unable to set record-type to variable-length records ****');
            PGM_$EXIT;
        END;

        process_sequential_file;

    END
    ELSE BEGIN
        rec_attrib.rec_lgth := record_size;
        rec_attrib.explicit_type := TRUE;
        rec_attrib.rec_type := STREAM_$F2;
        STREAM_$REDEFINE (rec_stream,[STREAM_$REC_LGTH,STREAM_$EXPLICIT_TYPE,
                          STREAM_$REC_TYPE],rec_attrib,rec_err,status);
        IF (status.all <> STATUS_$OK) THEN BEGIN
            WRITELN ('**** GETREC: Error - unable to set record-type to fixed-length records ****');
            PGM_$EXIT;
        END;

        process_direct_file;

    END;



    {We're done. Clean up and close files.}

    STREAM_$CLOSE (asc_stream,status);
    STREAM_$CLOSE (rec_stream,status);



{***** End of Program GETREC.PAS *****}
END.


End of getrec.pas
echo sendrec.bld 1>&2
cat >sendrec.bld <<'End of sendrec.bld'
von
pas sendrec.pas
bind -b sendrec sendrec.bin
voff
End of sendrec.bld
echo sendrec.doc 1>&2
cat >sendrec.doc <<'End of sendrec.doc'
*******************************************************************************
*****                                                                     *****
*****                           SENDREC.DOC                               *****
*****                            Version 6                                *****
*****                                                                     *****
*****        Programming Notes for the SENDREC and GETREC programs.       *****
*****                                                                     *****
*****      Copyright (c) 1986                                             *****
*****      David M. Krowitz                                               *****
*****      Massachusetts Institute of Technology                          *****
*****      Department of Earth, Atmosheric, and Planetary Sciences        *****
*******************************************************************************



Transferring Files over the Ethernet
------------------------------------

    NOTE:  The following discussion is based on using the AEGIS SR9.0
version of Apollo's TCP/IP ethernet package to transfer files to and
from an Alliant Computer Systems Corp. FX/1 or FX/8 computer running
Alliant's implementation of BSD 4.2 Unix and the Alliant Fortran
compiler *or* to a Sun-3 workstation. This discussion may or may not
be valid for other machines running BSD 4.2 or for Apollo's Domain IX
implementation of the ethernet software. Read the section on file
formats carefully to determine if this discussion would apply to another
machine. Apollo is  marketinf the FX/1 and FX/8 as computational
servers in their network, and they offer the machines with
NFS transparent file access software which would make some of these
file transfer issues disappear.

    Our geophysical research group at the MIT Earth, Atmospheric, and
Planetary sciences department runs a ring of about a dozen Apollo
workstations and file servers (all running AEGIS SR9.0) and an early
production model of the Alliant FX/1. We wish to be able to transfer
both ascii files (program source files, printouts, and Fortran data
files written using a FORMAT statement) and both direct-access and
sequential-access unformatted binary data files (Fortran record
structured files written without a FORMAT statement) to and from the
FX/1 using Apollo's implementation of the standard ethernet File
Transfer Program (FTP). 


Transferring Ascii Files with FTP
---------------------------------

    Sending ascii files to and from the FX/1 is quite easy. You simply
use the FTP to open a connection with the FX/1, set the transfer mode to
ascii (this is the default mode when you start FTP), and send (or get)
the file.  If you are sending an ascii file from the Apollo node to the
FX/1, the FTP program simply strips off the 32-byte AEGIS file header
from the Apollo file (a UASC file type) and sends the rest of the bytes
in the file to the Alliant machine which puts them into a file exactly
as they are. If you are getting an ascii file from the FX/1, the FTP
program simply gets the bytes from the file on the Alliant machine,
creates an AEGIS ascii file (UASC file type) and the 32-byte file
header, and puts the bytes into the file exactly as they are. The files
can be read or written by the Apollo and Alliant system utilities
(editors, file comparision programs, etc.) and by Fortran programs on
either machine without any further work on the user's part. A sample FTP
session showing how to transfer ascii files is shown below.

    $FTP KERMIT                 {Get A connection to the FX/1 named KERMIT}
    >LOG KROWITZ                {Get logged into an account on the FX/1}
    >Password:                  {Enter password for FX/1 account}
    >TYPE ASCII                 {Set transfer mode to ascii (default)}
    >SEND TEST.FTN TEST.F       {Send the TEST Fortran program to the file TEST.F}
    >SEND INPUT.DAT INPUT.DAT   {Send the ascii data file INPUT.DAT}
    >GET RESULT.DAT PRINT.OUT   {Get the ascii data file RESULT.DAT from the FX/1}
    >GET NEW.F NEW.FTN          {Get the NEW Fortran program from the FX/1}
    >GET NEWSUBS.F NEWSUBS.FTN  {And get its subroutines, too}
    >BYE                        {Logout from FX/1 and close the connection}
    $PRF PRINT.OUT              {Print the ascii file received from the FX/1}
    $FTN NEW.FTN                {Compile and bind the program from the FX/1}
    $FTN NEWSUBS.FTN
    $BIND -B NEW NEW.BIN NEWSUBS.BIN


Transferring Data Binary Files with FTP
---------------------------------------

    Sending record structured binary data files to and from the FX/1 is
a bit more complicated. Binary files on the Apollo have an AEGIS file
type of either REC for data files containing fixed or variable length
records, OBJ for object files produced by the compilers and the binder,
BITMAP for GPR bitmap files (GMF black and white bitmap files are record
structured files with a file type of REC), or NIL for GMR metafiles, GMR
vector graphics output files, font files, or other files which the
Apollo's stream I/O system does not know how to handle. Files on the
Alliant FX/1, on the other hand, don't have any file types or file
headers. Bytes are simply read or written to a file by the Unix I/O
system in the order in which they occur.

    There is little use in trying to transfer most of the types of
Apollo binary files to the Alliant. OBJ files would not be able to
execute on the FX/1 (despite the fact that it runs a 68020 based
instruction set) because of the difference in the operating systems and
the system libraries. The Alliant does not have a graphics screen, so
sending BITMAP files to the FX/1 isn't of much use either.  The REC
files, however, are useful on both the Apollo and the Alliant machines.
The REC file type is produced whenever a Fortran program on the Apollo
writes to a file without using a FORMAT statement.  The FX/1 does not
have file types like AEGIS does, but its Fortran programs can also do
unformatted READs and WRITEs to files.  Unformatted binary data files
are usually 2 or 3 times smaller than the same data file written out in
ascii. Users at our lab tend to be shipping around 50 or 60 megabytes of
data at a time, so being able to send record structured binary data
files to and from the Alliant machine is a high priority item for our
lab. Fortunately, both the Apollo machines and the Alliant FX/1 and FX/8
use the Motorola 68000 instruction set for their integer arithmetic and
the IEEE floating point format for their single and double precision
arithmetic. It is possible to send the data bytes in the records of a
file to the other machine without modification. We only have to worry
about the differences in the format of the data records on the Apollo,
the format of the records on the Alliant machines, and the file type on
the Apollo node. We also must take into consideration whether the file
was created with a Fortran direct-access unformatted WRITE statement
(eg. WRITE (unit,rec=record_number) A,B,C ) or with a Fortran 
sequential-access unformatted WRITE statement (eg. WRITE (unit) A,B,C )
since the Alliant FX/1 and FX/8 use a different file format for these
two kinds of files while the Apollo machines use the same file format
in both cases.

    Unfortunately, when you try to send an Apollo REC file to the
Alliant with the FTP program the following happens: the FTP program
strips off the 32-byte AEGIS file header from the binary data file and
get the first record from the file, it then removes the record count (a
4 byte integer telling the machine how many bytes of data are contained
in the record) from the record and sends the data bytes to the FX/1, the
data bytes are placed in the receiving file on the FX/1, and the next
record is read from the Apollo REC file, its record count is removed,
and remaining the data bytes in the record are sent to the FX/1 file.
The problem is that when a Fortran program on the Alliant machines tries
to do an unformatted READ or WRITE it expects to find a record count in
the file so that the Fortran I/O routines will know how many bytes of
data to read from the file. The format of the record count is somewhat
different on the Apollo and on the Alliant machines (see the section
below), but since the FTP program has completely thrown away the record
count information when it transmitted the Apollo REC file there is no
way of figuring out how to reformat the file once it has been sent. The
FTP program will strip the record counts out of the REC file records
regardless of whether the transfer mode was set to ascii, binary, or
image mode.

    When you try to receive a binary data file created by a Fortran
program with an unformatted WRITE statement on the Alliant, the Apollo
FTP program creates an AEGIS file and the 32-byte file header, gets the
bytes from the file on the FX/1 and puts them into the AEGIS file just
as they are (including the record counts produced by the FX/1 Fortran
program). If the transfer mode was ASCII or IMAGE, then the FTP program
creates a file with a UASC file type and header. If the transfer mode
was BINARY, then the FTP program creates a file with an OBJ file type
and header. The FTP program will not create an REC file no matter what
you do.  Fortunately, though, the file created by the FTP program will
contain all of the bytes in the file from the FX/1 including the record
counts. The information necessary for reformatting the data file from
the Alliant format to the AEGIS format has been preserved during the
transfer even though the FTP program did not create a file with an REC
file type.


Using SENDREC and GETREC to Transfer Binary Data Files with FTP
---------------------------------------------------------------

    In order to send a binary data file (REC file) over the ethernet to
the FX/1 we have to have a file with a UASC file type in order to
prevent the FTP program from trying to remove the record counts from the
file. In order to receive a binary data file from the FX/1 we have to
create a file with the REC file type from the UASC or OBJ file which the
FTP program created when it transferred the file. The SENDREC and GETREC
programs perform these functions in addition to reformatting the records
to take care of the differences in the format of binary data files on
the Apollo and the Alliant machines.

    The SENDREC program opens a binary data file (REC file type) created
on the Apollo, creates a UASC file and file header on the Apollo, and
then reads records from the REC file, modifies the format of the record
counts, and writes the records to the UASC file. The records are not
converted to ascii. The data in the record is written to the UASC file
exactly as it was found in the REC file. Since the SENDREC program
created the output file with a UASC file type, the FTP program will
simply strip the 32-byte file header off of the file before sending the
rest of the bytes in the file to the FX/1. The SENDREC program will
automatically determine whether the Apollo file is a direct-access
file or a sequential-access file from the information stored in the
file header and it will use the appropiate file format for the Alliant
output file (Fortran direct-access binary files have a different file
format than Fortran sequential-access files on the Alliant. On the
Apollo, they have identical record formats). The file can be read by
Fortran programs on the FX/1 as soon as the file has been sent by the
FTP program. A sample session of how to send a binary data file to the
FX/1 is shown below:

    $FTN WTEST.FTN                  {Compile and bind the WTEST program}
    $BIND -B WTEST WTEST.BIN
    $WTEST                          {Write the binary files TEST.SEQ.DAT
                                     (sequential-access binary file) and
                                     TEST.DIR.DAT (direct-access binary
                                     file).}
    $SENDREC                        {Reformat the 12 records of TEST.SEQ.DAT}
    This is SENDREC Version 4.

    Enter name of the AEGIS format file for input: TEST.SEQ.DAT
    Enter name of the Alliant FX/1 format file for output: TEST.SEQ.FX1
    Processing sequential-access input file.

    record:     1
    size:       18000

    record:     2
    size:       18000

    record:     3
    size:       18000

    record:     4
    size:       18000

    record:     5
    size:       18000

    record:     6
    size:       18000

    record:     7
    size:       18000

    record:     8
    size:       18000

    record:     9
    size:       20000

    record:     10
    size:       24000

    record:     11
    size:       28000

    record:     12
    size:       36000
    $SENDREC                        {Reformat the 12 records of TEST.DIR.DAT}
    This is SENDREC Version 4.

    Enter name of the AEGIS format file for input: TEST.DIR.DAT
    Enter name of the Alliant FX/1 format file for output: TEST.DIR.FX1
    Processing direct-access input file.

    record:     1
    size:       36000

    record:     2
    size:       36000

    record:     3
    size:       36000

    record:     4
    size:       36000

    record:     5
    size:       36000

    record:     6
    size:       36000

    record:     7
    size:       36000

    record:     8
    size:       36000

    record:     9
    size:       36000

    record:     10
    size:       36000

    record:     11
    size:       36000

    record:     12
    size:       36000
    $FTP KERMIT
    >LOG KROWITZ                        {Get logged into an account on the FX/1}
    >Password:                          {Enter password for FX/1 account}
    >TYPE ASCII                         {Set transfer mode to ascii (default)}
    >SEND TEST.SEQ.FX1 TEST.SEQ.DAT     {Send the reformatted files}
    >SEND TEST.DIR.FX1 TEST.DIR.DAT
    >BYE                                {Logout from FX/1 and close the connection}


    The GETREC program opens a binary data file which was created on the
Alliant and transferred to the Apollo by the FTP program using the ascii
transfer mode (ie. FTP put the file from the FX/1 into a UASC file),
creates a REC file and file header on the Apollo, and then reads records
from the UASC file, modifies the format of the record counts, and writes
the records to the REC file. The records from the FX/1 are not ascii,
but binary data.  The data in the record is written to the REC file
exactly as it was found in the UASC file. The GETREC program will ask the
user whether the Alliant Fortran output file was created as a sequential-
access file or as a direct-access file in order to determine which file
format it should use to read the input file. If the user indicates that
the Alliant file is a direct-access file then they will be asked to enter
the size in bytes of the fixed-length records. GETREC will then use the
appropiate file format for converting the Alliant input file. The output
file can be read by Fortran programs on the Apollo as soon as the GETREC
program has finished. A sample session of how to receive a binary data
file from the FX/1 is shown below:


    $FTP KERMIT
    >LOG KROWITZ                {Get logged into an account on the FX/1}
    >Password:                  {Enter password for FX/1 account}
    >TYPE ASCII                 {Set transfer mode to ascii (default)}
    >GET TEST.SEQ.DAT TEST.SEQ.FX1      {Get the Alliant format files}
    >GET TEST.DIR.DAT TEST.DIR.FX1
    >BYE                        {Logout from FX/1 and close the connection}
    $GETREC                     {Reformat the 12 records TEST.SEQ.FX1}
    This is GETREC Version 6.

    Enter name of Alliant/Sun format file for input: TEST.SEQ.FX1
    Enter name of AEGIS format file for output: TEST.SEQ.DAT

    Enter a D to process a direct-access file from the Alliant/Sun,
    Enter an S to process a sequential-access file from the Alliant/Sun: S

    record:     1
    size:       18000

    record:     2
    size:       18000

    record:     3
    size:       18000

    record:     4
    size:       18000

    record:     5
    size:       18000

    record:     6
    size:       18000

    record:     7
    size:       18000

    record:     8
    size:       18000

    record:     9
    size:       20000

    record:     10
    size:       24000

    record:     11
    size:       28000

    record:     12
    size:       36000
    $GETREC                     {Reformat the 12 records TEST.DIR.FX1}
    This is GETREC Version 6.

    Enter name of Alliant/Sun format file for input: TEST.DIR.FX1
    Enter name of AEGIS format file for output: TEST.DIR.DAT

    Enter a D to process a direct-access file from the Alliant/Sun,
    Enter an S to process a sequential-access file from the Alliant/Sun: D

    Enter length of direct-access record in bytes: 36000

    record:     1
    size:       36000

    record:     2
    size:       36000

    record:     3
    size:       36000

    record:     4
    size:       36000

    record:     5
    size:       36000

    record:     6
    size:       36000

    record:     7
    size:       36000

    record:     8
    size:       36000

    record:     9
    size:       36000

    record:     10
    size:       36000

    record:     11
    size:       36000

    record:     12
    size:       36000
    $FTN RTEST.FTN                  {Compile and bind the RTEST program}
    $BIND -B RTEST RTEST.BIN
    $RTEST                          {Check that TEST.DAT has the same format}
                                    {as the file which WTEST creates}


    As of version 5, both SENDREC and GETREC are capable of reading the
names of the input and output files from the command line. (ie. typing
SENDREC TEST.DAT TEST.FX1 and GETREC TEST.FX1 TEST.DAT will work). In
addition, the GETREC program now recognizes the switches '-D' and '-S'
to specify whether the input file is a direct-access or a sequential-
access binary file from the Alliant. If the '-D' switch is given, the
next item on the command line must be the record size of the direct-
access binary records (in bytes). If no filenames are found on the
command line SENDREC and GETREC will prompt the user for the file
names. If only one file name is found on the command line, it is
taken to be the input file and the user is prompted for the name of
the output file.


Restrictions on SENDREC and GETREC
----------------------------------

    There a two restrictions which users must follow in order to use
SENDREC and GETREC. The first is fairly simple: the Alliant Fortran
compiler allows double precision complex variables (ie. COMPLEX*16 or
DOUBLE COMPLEX variable declarations). The Apollo Fortran compiler does
not implement this extension to the Fortran-77 standard. Fortran's
complex numbers are simply ordered pairs of real numbers, and double
precision complex numbers are ordered pairs of double precision numbers.
Since both the FX1, the FX/8, and the Apollo machines use the IEEE
standard for floating point numbers the GETREC program can read and
reformat records containing double precision complex numbers without and
trouble.  A Fortran program on the Apollo which wishes to the the binary
data file will, however, have to take into account the fact that the
Apollo Fortran compiler does not have double precision complex
arithmetic. A sample FX/1 program to write a file of double precision
complex numbers and the corresponding Apollo program to read the double
precision numbers are shown below:

    FX/1:

          PROGRAM WDOUBLE
          COMPLEX*16 A,B,C(10)

          A = (-1.0D0,0.0D0)
          B = (0.0D0,-1.0D0)
          DO 10 I = 1,10
    10    C(I) = A*B

          OPEN (UNIT=1,FILE='TEST.DAT',RECL=1000,FORM='UNFORMATTED'
         &,STATUS='UNKNOWN')
          WRITE (1) A,B,C

          STOP
          END


    Apollo:

          PROGRAM RDOUBLE
          DOUBLE PRECISION A(2),B(2),C(2,10)
          DOUBLE PRECISION ATEST(2),BTEST(2),CTEST(2,10)

          ATEST(1) = -1.0D0
          ATEST(2) = 0.0D0
          BTEST(1) = 0.0D0
          BTEST(2) = -1.0D0
          DO 10 I = 1,10
          CTEST(1,I) = ATEST(1)*BTEST(1)-ATEST(2)*BTEST(2)
    10    CTEST(2,I) = ATEST(1)*BTEST(2)+ATEST(2)*BTEST(1)

          OPEN (UNIT=1,FILE='TEST.DAT',RECL=1000,FORM='UNFORMATTED'
         &,STATUS='OLD')
          READ (1) A,B,C

          IF ((ATEST(1).NE.A(1)).OR.(ATEST(2).NE.A(2)) THEN
            WRITE(*,100)
    100     FORMAT (1X,'VARIABLE A NOT READ CORRECTLY')
          ENDIF

          IF ((BTEST(1).NE.B(1)).OR.(BTEST(2).NE.B(2)) THEN
            WRITE(*,200)
    200     FORMAT (1X,'VARIABLE B NOT READ CORRECTLY')
          ENDIF

          DO 400 I = 1,10
          IF ((CTEST(1,I).NE.C(1,I)).OR.(CTEST(2,I).NE.C(2,I)) THEN
            WRITE(*,300)
    300     FORMAT (1X,'VARIABLE C NOT READ CORRECTLY')
          ENDIF
    400   CONTINUE

          STOP
          END

    The second problem is a little more subtle and affects both the
SENDREC and GETREC programs. If the user declares an odd length
character string in a Fortran program (eg. a CHARACTER*7 variable
declaration), and writes it to the binary data file the Fortran I/O
routines will pad the character string so that the next numerical item
in the file (ie. an INTEGER, REAL, or COMPLEX variable in the program,
or the record count of the next record) will start on a even-numbered
byte in the file. If an odd length character string is followed by an
even length character string, the Fortran I/O routines will pad the end
of the even length string so that the next number (or record count) will
start on an even-numbered byte in the file. If an odd length character
string is followed by another odd length character string, then the
Apollo's Fortran I/O routines do not have to pad the end of the second
string since the next item in the file is already on an even-numbered
byte. Note that this padding only occurs for character strings which are
DECLARED with an odd length. A string which is declared to have an
odd number of characters as its maximum length may always contain an
even number of characters, and a string which is declared to have an
even number of characters as its maximum length may contain an even
or odd number of characters up to that maximum. Also note that character
strings which are stored in INTEGER*2, INTEGER*4, REAL*4, REAL*8, DOUBLE
PRECISION, or COMPLEX variables are automatically even length character
strings since all of these data types take up an even number of bytes of
storage. The only way to declare a character string with an odd length
is to use the Fortran data type CHARACTER*n where n is an odd number.

    The problem with odd length character strings is that the Alliant's
Fortran I/O routines do not attempt to pad odd length strings. The
Alliant's Fortran I/O routines don't care whether or not the numerical
items in the file are aligned on even-numbered bytes. As a result,
records which contain odd length character strings will cause the data
in the file to get out of alignment when the file is transferred to or
from the FX/1. There is no way for the GETREC and SENDREC programs to
get around this problem.  The records in a binary data file contain no
information as to what kind of data is in the record. They only contain
the record counts and the raw bytes of the data. This is why they are
smaller and faster to read and write than formatted (ascii) Fortran
files. The user simply must avoid writing character strings which have
been declared with an odd length into files which are going to be
transferred to or from the FX/1.
                   

    The following sample programs are OK.

          PROGRAM TEST1
          REAL A,B(10)
          INTEGER*2 STRING(10)          {I*2 always even length}
          INTEGER*2 IA,IB

          READ(*,10) STRING             {Even character string}
    10    FORMAT (10A2)
          READ(*,20) IA,IB
    20    FORMAT (A1,5X,A1)             {Odd strings stored in even}

          WRITE (1) A,B,STRING,IA,IB

          STOP
          END


          PROGRAM TEST2
          REAL A,B(10)
          CHARACTER*20 STRING
          INTEGER*2 IA,IB

          READ(*,10) STRING             {Even Character string}
    10    FORMAT (A20)

          WRITE (1) A,B,STRING,IA,IB

          STOP
          END


          PROGRAM TEST3
          REAL A,B(10)
          INTEGER*4 STRING1(5)
          INTEGER*2 IA,IB
          CHARACTER*20 STRING2

          READ(*,10) STRING1            {Even Character string}
    10    FORMAT (5A4)
          READ(*,20) STRING2            {Read odd length into even string}
    20    FORMAT (A17)

          WRITE (1) A,B,STRING1,IA,STRING2,IB

          STOP
          END 


    The following sample programs will create data files which
    can not be transferred correctly.

          PROGRAM BAD1
          REAL A,B(10)
          CHARACTER*17 STRING           {Odd length string}
          INTEGER*2 IA,IB

          READ(*,10) STRING             {Odd character string}
    10    FORMAT (10A2)

          WRITE (1) A,B,STRING,IA,IB    {Apollo will try to put IA}
                                        {on an even byte boundry}
          STOP
          END


          
          PROGRAM BAD2
          REAL A,B(10)
          CHARACTER*20 STR1             {Even length string}
          CHARACTER*17 STR2             {Odd length string}
          INTEGER*2 IA,IB

          READ(*,10) STR1               {Read odd string into even var}
    10    FORMAT (A17)
          READ(*,20) STR2               {Read odd string into odd var}
    20    FORMAT (A17)

          WRITE (1) A,B,STR1,STR2,IA,IB     {Apollo will try to put IA}
                                            {on an even byte boundry}
          STOP
          END


          PROGRAM BAD3
          REAL A,B(10)
          CHARACTER*20 STR1             {Even length string}
          CHARACTER*17 STR2             {Odd length string}
          INTEGER*2 IA,IB

          READ(*,10) STR1               {Odd character string}
    10    FORMAT (A17)
          READ(*,20) STR2               {Read even string into even var}
    20    FORMAT (A18)

          WRITE (1) A,B,STR2,IA,IB,STR1     {Apollo will try to put next}
                                            {record on an even byte boundry}
          STOP
          END


    The following programs will create data files which will
    transfer correctly, but they are risky business at best.


          PROGRAM MAYBE1
          REAL A,B(10)
          CHARACTER*17 STR1             {Odd length string}
          CHARACTER*13 STR2             {Odd length string}
          INTEGER*2 IA,IB

          READ(*,10) STR1               {Read odd string into odd var}
    10    FORMAT (A17)
          READ(*,20) STR2               {Read even string into odd var}
    20    FORMAT (A10)

          WRITE (1) A,B,STR1,STR2,IA,IB {Apollo will not pad STR1 and }
                                        {STR2 ends on an even byte boundry}
          STOP
          END


          PROGRAM MAYBE2
          REAL A,B(10)
          CHARACTER*20 STR1             {Even length string}
          CHARACTER*17 STR2             {Odd length string}
          CHARACTER*13 STR3             {Odd length string}
          INTEGER*2 IA,IB

          READ(*,10) STR1               {Read odd string into even var}
    10    FORMAT (A17)
          READ(*,20) STR2               {Read even string into odd var}
    20    FORMAT (A16)
          READ(*,30) STR3               {Read even string into odd var}
    30    FORMAT (A10)

          WRITE (1) A,B,STR1,STR2,      {Apollo will not pad STR1 or STR2 and}
         &STR3,IA,IB                    {STR3 ends on an even byte boundry}

          STOP
          END




Apollo and Alliant/Sun Sequential-Access Binary File Formats
--------------------------------------------------------

    All AEGIS files on the Apollo workstation have a 32-byte long header
which precedes the data in the file. Each different kind of AEGIS file
(ie. REC, UASC, etc) has a different file header. Files on the Alliant
machines seem to be standard BSD 4.2 Unix files. They have no file
headers or file types. The Alliant files seem to be *exactly* the same
as files created on a Sun-3 workstation. A file comparision of the files
created by the WTEST (write test files) program running on our Alliant FX/1
and our Sun-3/160 workstation with the Motorola 68881 FPA shows no
differences.

    A sequential-access unformatted binary data file on the Apollo
(an REC file type) has a 32-byte header followed by any number of fixed
or variable length records. Each record is word aligned (ie. it starts
on an even numbered byte in the file). The first four bytes of the
record are a 32-bit integer containing the total number of bytes in the
record (including both the data which the user wrote into the record and
the 4-byte long record count itself). All integer and floating point
data in the record (ie. any INTEGER*2, INTEGER*4, REAL*4, REAL*8,
DOUBLE PRECISION, or COMPLEX variables) are word aligned. That is, each
item begins on an even number byte in the file. Character strings
(ie. CHARACTER*n where 'n' is the maximum length of the string) are
not word aligned. They may start on either an even or an odd numbered
byte. A character string which ends on an odd-numbered byte will be
padded with an extra character if it is followed by an integer or
floating point number. If the string is followed by another character
string, it will not be padded, but if the second string also ends on
an odd-numbered byte, the second string may be padded. If the character
string is the last item in the record and it ends on an odd-numbered
byte, then the string is not padded in the sense that the record count
will contain an odd number, but it is padded in the sense that the next
record will begin on the next even-numbered byte. Note that character
strings which are stored in integer or floating point variables are
always even length character strings. The only way to get an odd-length
variable is to declare the variable as CHARACTER*n, where 'n' is an
odd number.

    An unformatted sequential-access binary data file on the Alliant
or has the Sun has no file header. It consists of any number of fixed
or variable length records. The first four bytes of the record are a
32-bit integer containing the number of bytes of data in the record
not including the record count.  (ie. the Alliant/Sun record count is
the Apollo record count minus four) The record count is followed by
the data which the user wrote into the record. The items in the data
are not word aligned. All types of data may start on either an
even-numbered or an odd-numbered byte in the file. The data in the
record is followed by a second copy of the four byte long record count
which occurred at the beginning of the record. Thus, the record in the
Alliant/Sun file is four bytes longer than the corresponding record on
the Apollo even though the contents of the byte count on the Alliant/Sun
is four less than the contents of the byte count on the Apollo. Records
on the Alliant/Sun are not word aligned as they are on the Apollo.

    The following example should help in understanding the differences
between the two file formats:

      PROGRAM SAMPLE
      INTEGER*2 A,B
      INTEGER*4 AA,BB
      REAL*4 RA,RB
      CHARACTER*3 ODD
      CHARACTER*4 EVEN

      A = 1
      B = 2
      AA = 100
      BB = 'FOO'                    {3 characters in a 4-byte integer}
      RA = 15.5
      RB = 'BAR'                    {3 characters in a 4-byte floating point}
      RA = 1.0  
      RB = 2.0  
      ODD = 'ODD'
      EVEN = 'EVEN'

      WRITE (1) A,B,AA,BB,RA,RB      
      WRITE (1) A,B,ODD,A,B
      WRITE (1) A,B,EVEN,A,B
      WRITE (1) A,B,ODD,EVEN,A,B
      WRITE (1) A,B,ODD
      WRITE (1) A,B,EVEN
      WRITE (1) A,B


Byte    Apollo Format (Hex)                     Alliant or Sun-3 Format (Hex)
----  -------------                           --------------

      ===========                             ===========
0     |  header |                             |  00 00  |    Record 1 Count 
      |---------|                             |         |
2     |  header |                             |  00 14  |    20 bytes long
      |---------|                             |---------|
4     |  header |                             |  00 01  |    1 (INTEGER*2)
      |---------|                             |---------|
6     |  header |                             |  00 02  |    2 (INTEGER*2)
      |---------|                             |---------|
8     |  header |                             |  00 00  |  100 (INTEGER*4)
      |---------|                             |         |
10    |  header |                             |  00 64  |
      |---------|                             |---------|
12    |  header |                             | 'F' 'O' |  FOO (INTEGER*4)
      |---------|                             |         |
14    |  header |                             | 'O' ' ' |  Note padded with space
      |---------|                             |---------|
16    |  header |                             |  3F 80  |  1.0 (REAL*4)
      |---------|                             |         |
18    |  header |                             |  00 00  |
      |---------|                             |---------|
20    |  header |                             |  40 00  |  2.0 (REAL*4)
      |---------|                             |         |
22    |  header |                             |  00 00  |
      |---------|                             |---------|
24    |  header |                             |  00 00  |    Record 1 Count 
      |---------|                             |         |
26    |  header |                             |  00 14  |    second copy
      |---------|                             ===========
28    |  header |                             |  00 00  |    Record 2 Count
      |---------|                             |         |
30    |  header |                             |  00 0B  |    11 bytes long
      ===========                             |---------|
32    |  00 00  |    Record 1 Count           |  00 01  |    1 (INTEGER*2)
      |         |                             |---------|
34    |  00 18  |    20+4 bytes long          |  00 02  |    2 (INTEGER*2)
      |---------|                             |---------|
36    |  00 01  |    1 (INTEGER*2)            | 'O' 'D' |  ODD (CHARACTER*3)
      |---------|                             |    -----|
38    |  00 02  |    2 (INTEGER*2)            | 'D'| 00 |    1 (INTEGER*2)
      |---------|                             |---------|
40    |  00 00  |  100 (INTEGER*4)            |  01| 00 |    2 (INTEGER*2)
      |         |                             |---------|
42    |  00 64  |                             |  02| 00 |    Record 2 Count
      |---------|                             |-----    |
44    | 'F' 'O' |  FOO (INTEGER*4)            |  00  00 |    second copy
      |         |                             |    ======
46    | 'O' ' ' |   Note padded with space    |  0A| 00 |    Record 3 Count
      |---------|                             ======    |
48    |  3F 80  |  1.0 (REAL*4)               |  00  00 |    12 bytes long
      |         |                             |    -----|
50    |  00 00  |                             |  0C| 00 |    1 (INTEGER*2)
      |---------|                             |---------|
52    |  40 00  |  2.0 (REAL*4)               |  01| 00 |    2 (INTEGER*2)
      |         |                             |---------|
54    |  00 00  |                             |  02| 'E'| EVEN (CHARACTER*4)
      ===========                             |-----    |
56    |  00 00  |    Record 2 Count           | 'V' 'E' |
      |         |                             |    -----|
58    |  00 10  |    12+4 bytes long          | 'N'| 00 |    1 (INTEGER*2)
      |---------|                             |---------|
60    |  00 01  |    1 (INTEGER*2)            |  01| 00 |    2 (INTEGER*2)
      |---------|                             |---------|
62    |  00 02  |    2 (INTEGER*2)            |  02| 00 |    Record 3 Count
      |---------|                             |-----    |
64    | 'O' 'D' |  ODD (CHARACTER*3)          |  00 00  |    second copy
      |    -----|                             |    ======
66    | 'D'| x  |   Note padding to word      |  0C| 00 |    Record 4 Count
      |---------|   align next integer        ======    |
68    |  00 01  |    1 (INTEGER*2)            |  00 00  |    15 bytes long
      |---------|                             |    -----|
70    |  00 02  |    2 (INTEGER*2)            |  0F| 00 |    1 (INTEGER*2)
      ===========                             |---------|
72    |  00 00  |    Record 3 Count           |  01| 00 |    2 (INTEGER*2)
      |         |                             |---------|
74    |  00 10  |    12+4 bytes long          |  02| 'O'|  ODD (CHARACTER*3)
      |---------|                             |-----    |
76    |  00 01  |    1 (INTEGER*2)            | 'D' 'D' |
      |---------|                             |---------|
78    |  00 02  |    2 (INTEGER*2)            | 'E' 'V' | EVEN (CHARACTER*4)
      |---------|                             |         |
80    | 'E' 'V' | EVEN (CHARACTER*4)          | 'E' 'N' |
      |         |                             |---------|
82    | 'E' 'N' |                             |  00 01  |    1 (INTEGER*2)
      |---------|                             |---------|
84    |  00 01  |    1 (INTEGER*2)            |  00 02  |    2 (INTEGER*2)
      |---------|                             |---------|
86    |  00 02  |    2 (INTEGER*2)            |  00 00  |    Record 4 Count
      ===========                             |         |
88    |  00 00  |    Record 4 Count           |  00 0F  |    second copy
      |         |                             ===========
90    |  00 13  |    15+4 bytes long          |  00 00  |    Record 5 Count
      |---------|                             |         |
92    |  00 01  |    1 (INTEGER*2)            |  00 07  |    7 bytes long
      |---------|                             |---------|
94    |  00 02  |    2 (INTEGER*2)            |  00 01  |    1 (INTEGER*2)
      |---------|                             |---------|
96    | 'O' 'D' |  ODD (CHARACTER*3)          |  00 02  |    2 (INTEGER*2)
      |    -----|                             |---------|
98    | 'D'|'E' | EVEN (CHARACTER*4)          | 'O' 'D' |  ODD (CHARACTER*3)
      |-----    |                             |    -----|
100   | 'V' 'E' |                             | 'D'| 00 |    Record 5 Count
      |    -----|                             |-----    |
102   | 'N'| x  |   Note padding to word      |  00 00  |    second copy
      |---------|   align next integer        |    ======
104   |  00 01  |    1 (INTEGER*2)            |  07| 00 |    Record 6 Count
      |---------|                             ======    |
106   |  00 02  |    2 (INTEGER*2)            |  00 00  |    8 bytes long
      ===========                             |    -----|
108   |  00 00  |    Record 5 Count           |  08| 00 |    1 (INTEGER*2)
      |         |                             |---------|
110   |  00 0B  |    7+4 bytes long           |  01| 00 |    2 (INTEGER*2)
      |---------|                             |---------|
112   |  00 01  |    1 (INTEGER*2)            |  02| 'E'| EVEN (CHARACTER*4)
      |---------|                             |-----    |
114   |  00 02  |    2 (INTEGER*2)            | 'V' 'E''|
      |---------|                             |    -----|
116   | 'O' 'D' |  ODD (CHARACTER*3)          | 'N'| 00 |    Record 6 Count
      |    -----|                             |-----    |
118   | 'D'| x  |   Note padding to word      |  00 00  |    second copy
      ===========   align next record         |    ======
120   |  00 00  |    Record 6 Count           |  08| 00 |    Record 7 Count
      |         |                             ======    |
112   |  00 0C  |    8+4 bytes long           |  00 00  |    4 bytes long
      |---------|                             |    -----|
114   |  00 01  |    1 (INTEGER*2)            |  04| 00 |    1 (INTEGER*2) 
      |---------|                             |---------|
116   |  00 02  |    2 (INTEGER*2)            |  01| 00 |    2 (INTEGER*2)
      |---------|                             |---------|
118   | 'E' 'V' | EVEN (CHARACTER*4)          |  02| 00 |    Record 7 Count
      |         |                             |-----    | 
120   | 'E' 'N' |                             |  00 00  |    second copy
      ===========                             |    ======
122   |  00 00  |    Record 7 Count           |  04|       Note file contains an
      |         |                             ======       odd number of bytes, AEGIS
124   |  00 08  |    4+4 bytes long                        REC files always contain an
      |---------|                                          even number of bytes.
126   |  00 01  |    1 (INTEGER*2)            
      |---------|                             
128   |  00 02  |    2 (INTEGER*2)            
      ===========                             



Apollo and Alliant/Sun Direct-Access Binary File Formats
----------------------------------------------------

    Direct-access binary files on the Apollo's have exactly the same file
format as sequential-access files. The only difference is that there is a
couple of bits set in the file header which indicate that the file has
fixed-length records and the length of the longest record in the file
(which is also stored in the file header) is the same as the record count
for each of the records in the file. Each record in the file has the same
32-bit record count (which contains the length of the fixed-length records
defined in the Fortran OPEN statement plus the 4-bytes of the count itself)
followed by the data portion of the record. If the number of bytes of data
written in the Fortran WRITE statement is less than the length of the record
defined in the OPEN statement, then the remainder of the record is packed
with whatever random garbage happens to be in the memory locations being
used by the system for mapping the file.

    Direct-access binary files on the Alliant and the Sun-3 have a different
file format than sequential-access files. Where the records in a sequential-
access file contain a record-count followed by the data portion of the record
followed by a second copy of the record count, the records in a direct-access
file do not contain any record counts at all! The direct-access file only
contains the data portion of the records. If the number of bytes of data
written by the Fortran WRITE statement is less than the size of the records
defined in the OPEN statement, then the remainder of the record is packed
with zeros.

    There are a couple of consequences to this format. The first is that if
a program on the Alliant/Sun writes a direct-access file with a record size
of 100 bytes another program on the Alliant or a Sun-3 could open that file
with a record size of 512 bytes, modify a few 512-bytes records, and close
the file. This would, of course, completely screw up the records written by
the first program. On the Apollo's this can not happen since the I/O system
will not let you open a direct-access file with a record size that is
different from the size which was used to create the file.  The second
consequence is that if the same file is created on both the Apollo and the
Alliant/Sun, and the Alliant/Sun file is brought over to the Apollo and
converted into the Apollo format with GETREC and it is then compared to
the Apollo file, the files will in general be different because of the
packing of the unused portions of the records. A Fortran program will
be able to read and write the file with no problems, but if it tries to read
more data from the record than the original program wrote then it will get
into trouble.

    The following example should help in understanding the differences
between the two file formats (Note that the Apollo direct-access format
shown here is the same as the sequential-access format shown above):
                                              
      PROGRAM SAMPLE
      INTEGER*2 A,B
      INTEGER*4 AA,BB
      CHARACTER*3 ODD
      CHARACTER*4 EVEN

      A = 1
      B = 2
      AA = 100
      BB = 'FOO'                    {3 characters in a 4-byte integer}
      ODD = 'ODD'
      EVEN = 'EVEN'
                         
      OPEN (UNIT=1,RECL=12)
      WRITE (1,REC=1) A,B,AA,BB
      WRITE (1,REC=2) A,B
      WRITE (1,REC=3) A,B,EVEN,A,B
      WRITE (1,REC=4) A,B,ODD,A,B
      WRITE (1,REC=5) A,B,ODD
      WRITE (1,REC=6) A,B,EVEN


Byte    Apollo Format (Hex)                     Alliant or Sun-3 Format (Hex)
----  -------------                           --------------

      ===========                             ===========
0     |  header |                             |  00 01  |    1 (INTEGER*2)  
      |---------|                             |---------|
2     |  header |                             |  00 02  |    2 (INTEGER*2)
      |---------|                             |---------|
4     |  header |                             |  00 00  |  100 (INTEGER*4)
      |---------|                             |         |
6     |  header |                             |  00 64  |                  
      |---------|                             |---------|
8     |  header |                             | 'F' 'O' |  FOO (INTEGER*4)
      |---------|                             |         |
10    |  header |                             | 'O' ' ' |  Note padded with space
      |---------|                             ===========
12    |  header |                             |  00 01  |    1 (INTEGER*2)
      |---------|                             |---------|
14    |  header |                             |  00 02  |    2 (INTEGER*2)       
      |---------|                             |---------|
16    |  header |                             |  00 00  |  Note that rest of
      |---------|                             |         |  record (8 bytes) is
18    |  header |                             |  00 00  |  padded with zeros
      |---------|                             |         |
20    |  header |                             |  00 00  |              
      |---------|                             |         |
22    |  header |                             |  00 00  |
      |---------|                             ===========
24    |  header |                             |  00 01  |    1 (INTEGER*2)  
      |---------|                             |---------|
26    |  header |                             |  00 02  |    2 (INTEGER*2)
      |---------|                             |---------|
28    |  header |                             | 'E' 'V' | EVEN (CHARACTER*4)
      |---------|                             |         |
30    |  header |                             | 'E' 'N' |                  
      ===========                             |---------|
32    |  00 00  |    Record 1 Count           |  00 01  |    1 (INTEGER*2)
      |         |                             |---------|
34    |  00 10  |    12+4 bytes long          |  00 02  |    2 (INTEGER*2)
      |---------|                             ===========
36    |  00 01  |    1 (INTEGER*2)            |  00 01  |    1 (INTEGER*2)  
      |---------|                             |---------|
38    |  00 02  |    2 (INTEGER*2)            |  00 02  |    2 (INTEGER*2)
      |---------|                             |---------|
40    |  00 00  |  100 (INTEGER*4)            | 'O' 'D' |  ODD (CHARACTER*3)
      |         |                             |    -----|
42    |  00 64  |                             | 'D'| 00 |    1 (INTEGER*2) 
      |---------|                             |-----    |
44    | 'F' 'O' |  FOO (INTEGER*4)            |  01| 00 |    2 (INTEGER*2)
      |         |                             |    -----|
46    | 'O' ' ' |  Note padded with space     |  02| 00 |  Note padded with 1 byte
      ===========                             ===========
48    |  00 00  |    Record 2 Count           |  00 01  |    1 (INTEGER*2)
      |         |                             |---------|
50    |  00 10  |    12+4 bytes long          |  00 02  |    2 (INTEGER*2)  
      |---------|                             |---------|
52    |  00 01  |    1 (INTEGER*2)            | 'O' 'D' |                   
      |---------|                             |    -----|
54    |  00 02  |    2 (INTEGER*2)            | 'D'| 00 |  Note padded with 5 bytes
      |---------|                             |-----    |
56    |   x  x  |  Note padded with 8         |  00 00  |
      |         |  bytes of random garbage    |         |
58    |   x  x  |                             |  00 00  |                  
      |         |                             =========== 
60    |   x  x  |                             |  00 01  |    1 (INTEGER*2)
      |         |                             |---------|
62    |   x  x  |                             |  00 02  |    2 (INTEGER*2) 
      ===========                             |---------|
64    |  00 00  |    Record 3 Count           | 'E' 'V' | EVEN (CHARACTER*4)
      |         |                             |         |
66    |  00 10  |    12+4 bytes               | 'E' 'N' |                  
      |---------|                             |---------|
68    |  00 01  |    1 (INTEGER*2)            |  00 00  |  Note padded with 4 bytes
      |---------|                             |         |
70    |  00 02  |    2 (INTEGER*2)            |  00 00  |                 
      |---------|                             ===========
72    | 'E' 'V' | EVEN (CHARACTER*4)                                           
      |         |                                        
74    | 'E' 'N' |                                                           
      |---------|                                        
76    |  00 01  |    1 (INTEGER*2)                       
      |---------|                                        
78    |  00 02  |    2 (INTEGER*2)                                          
      ===========                                        
80    |  00 00  |    Record 4 Count)                     
      |         |                                        
82    |  00 10  |    12+4 bytes long                                      
      |---------|                                        
84    |  00 01  |    1 (INTEGER*2)                                        
      |---------|                                        
86    |  00 02  |    2 (INTEGER*2)                                         
      |---------|                                        
88    | 'O' 'D' |  ODD (CHARACTER*3)                                    
      |    -----|                                        
90    | 'D'|  x |  Note padding to word                                        
      |---------|  align next integer                                     
92    |  00 01  |    1 (INTEGER*2)                     
      |---------|                                        
94    |  00 02  |    2 (INTEGER*2)                                         
      ===========                                        
96    |  00 00  |    Record 5 Count                                       
      |         |                                        
98    |  00 10  |    12+4 bytes long                                        
      |---------|                                        
100   |  00 01  |    1 (INTEGER*2)                                         
      |---------|                                        
102   |  00 02  |    2 (INTEGER*2)                                       
      |---------|                                                        
104   | 'O' 'D' |  ODD (CHARACTER*3)                                      
      |    -----|                                                           
106   | 'D'| x  |                                                         
      |-----    |                                                        
108   |   x  x  |  Note padded with 5 bytes                               
      |         |  of random garbage                                                         
110   |   x  x  |                                                         
      ===========                                                             
112   |  00 00  |    Record 6 Count                                       
      |         |                                        
114   |  00 10  |    12+4 bytes long                                        
      |---------|                                        
116   |  00 01  |    1 (INTEGER*2)                                         
      |---------|                                        
118   |  00 02  |    2 (INTEGER*2)                                       
      |---------|                                                        
120   | 'E' 'V' | EVEN (CHARACTER*4)                                      
      |         |                                                           
122   | 'E' 'N' |                                                         
      |---------|                                                        
124   |   x  x  |  Note padded with 4 bytes                               
      |         |  of random garbage                                                         
126   |   x  x  |                                                         
      ===========                                                             



                                              
                                              
                                              
Differences Between Program Versions
-------------------------------------

Version 3
---------

    A bug in the GETREC program which used to cause "access violation"
errors to sometimes occur in long files with large (15 Kb) records has
been fixed. GETREC used to do three STREAM_$GET_BUF calls in a row to
read the first copy of the Alliant record count, the data portion of the
Alliant record, and the second copy of the Alliant record count. Then it
would compare the two record counts for consistency before writing the
data portion of the Alliant record to the output file. The bug occurred
when the call to read the second copy of the record count resulted in
the stream I/O manager unmapping the data portion of the Alliant record
when the manager was using "locate-mode" to read the input file. This
has been fixed by reading both the data portion of the Alliant record and
the second copy of the record count with a single STREAM_$GET_BUF call
and then extracting the record count from the input buffer. Since there
no longer is a second STREAM call following the reading of the data
portion of the record there is no chance for the memory in which the
data was mapped to get unmapped by the stream manager before it has
been written to the output file.


Version 4
---------

    Both GETREC and SENDREC can now convert direct-access binary data
files in addition to the sequential-access files which versions 2 and 3
could convert.


Version 5
---------

    Both GETREC and SENDREC now are able to read the input and output
file names from the command line (eg. GETREC FILE.FX1 FILE.APOLLO).
If no file names are found on the command line then GETREC and SENDREC
will prompt the user for them. If only one file name is found, then
the user is prompted for the output file name. In addition GETREC
will recognize the switches '-D' and '-S' on the command line to
specify whether the input file is a direct-access or a sequential-
access binary file. If the '-D' switch is given, then the next item
on the command line is taken as the record size of the direct-access
file. If no switch is given, GETREC will prompt the user for the
file type.


Version 6
---------

    Tested GETREC and SENDREC with Sun-3/160. Files seems to be exactly
the same format as Alliant FX/1 and FX/8. Updated documentation. Both
programs tested with Apollo V2 demo (transparent file access to BSD 4.2
Unix machines) and beta-test version of Apollo implementation of NFS.



Files Needed to Build the SENDREC and GETREC programs
-----------------------------------------------------
                                              
    The files which are provided for the SENDREC and GETREC programs are:
                                              
        SENDREC.DOC                 - This file.
        SENDREC.DOC.INSTALL         - Notes on how to install the programs.
        SENDREC.PAS                 - The Pascal sources for the SENDREC program.
        GETREC.PAS                  - The Pascal sources for the GETREC program.
        SENDREC.BLD                 - A shell script file for compiling and
                                      binding the SENDREC program.
        GETREC.BLD                  - A shell script file for compiling and
                                      binding the GETREC program.
        SENDREC                     - A ready to run copy of the SENDREC program.
                                      Just in case you don't have a Pascal compiler.
                                      This is the file which is produced by
                                      SENDREC.BLD.
        GETREC                      - A ready to run copy of the GETREC program.
                                      Just in case you don't have a Pascal compiler.
                                      This is the file which is produced by
                                      GETREC.BLD.
        WTEST.FTN                   - A Fortran program to write the binary data files
                                      TEST.SEQ.DAT and TEST.DIR.DAT for use in testing.
        RTEST.FTN                   - A Fortran program to read TEST.SEQ.DAT and
                                      TEST.DIR.DAT after they have been sent over the
                                      ethernet and check that they contain the same
                                      data as was written by WTEST.
        WTEST.BLD                   - A shell script file for compiling and
                                      binding the WTEST program.
        RTEST.BLD                   - A shell script file for compiling and
                                      binding the RTEST program.
        WTEST                       - A ready to run copy of the WTEST program.
                                      Just in case you don't have a Fortran compiler.
        RTEST                       - A ready to run copy of the RTEST program.
                                      Just in case you don't have a Fortran compiler.

    You will also need the following standard Apollo-supplied files:

        /SYS/INS/BASE.INS.PAS       - These are all standard insert files which
        /SYS/INS/STREAMS.INS.PAS    - are used by SENDREC.PAS and GETREC.PAS
        /SYS/INS/PGM.INS.PAS


Bugs, Questions, and Improvements
---------------------------------

    If you find a bugs in the programs, have questions on how to install or
use them, or have a good idea for improving the programs please feel free to
contact me at the address below.


            David M. Krowitz
            MIT dept. of Earth, Atmospheric, and Planetary Sciences
            Room 54-527
            Cambridge, MA 02139
            (617) 253-6180


network mailing address:

            mit-erl!mit-kermit!krowitz@eddie.mit.edu
            mit-erl!mit-kermit!krowitz@mit-eddie.arpa
            david@mit-mc.arpa
            (in order of decreasing preference)

End of sendrec.doc

# To unbundle, sh this file
echo sendrec.hlp 1>&2
cat >sendrec.hlp <<'End of sendrec.hlp'
5.0;sendrec (send_record), revision 5.0, 86/08/12
SENDREC (SEND_RECORD) -- Convert a record-structured file to DSP9000 or Sun workstation format.
usage:  SENDREC [input file name] [output file name]


FORMAT

  SENDREC  [input file] [output file]

  SENDREC reads an REC format binary data file created by an Apollo
  node and converts the file into a record-structured binary data
  file which can be read by a DSP9000 (an Alliant FX/1 or FX/8) or
  a Sun workstation.


ARGUMENTS

  input file         Name of the Apollo REC format file to be converted. If
  (optional)         not given on the command line the user will be prompted
                     for the input file name.

  output file        Name of the DSP9000/Sun format file to be written. If not
  (optional)         given on the command line the user will be prompted for
                     the output file name.



EXAMPLES

  1.  $ sendrec data.apollo data.fx1                Converting a sequential-access
        This is SENDREC Version 5.                  format Apollo REC file into a
                                                    DSP9000/Sun format file.
        Processing sequential-access input file.

        record:     1
        size:       1500

        record:     2
        size:       1500

        record:     3
        size:       1200
                .
                .
                .

  2.  $ sendrec data.apollo                         Converting a sequential-access
        This is SENDREC Version 5.                  format Apollo REC file into a
                                                    DSP9000/Sun format file.
        Enter name of the Alliant FX/1 format file for output: data.fx1
        Processing sequential-access input file.

        record:     1
        size:       18000

        record:     2
        size:       36000

        record:     3
        size:       12000
                .
                .
                .

  3.  $ sendrec                                     Converting a direct-access
        This is SENDREC Version 5.                  format Apollo REC file into a
                                                    DSP9000/Sun format file.
        Enter name of the AEGIS format file for input: data.apollo
        Enter name of the Alliant FX/1 format file for output: data.fx1
        Processing direct-access input file.

        record:     1
        size:       36000

        record:     2
        size:       36000

        record:     3
        size:       36000
                .
                .
                .

End of sendrec.hlp
echo sendrec.pas 1>&2
cat >sendrec.pas <<'End of sendrec.pas'
{*****************************************************************************
 *****                                                                   *****
 *****                            SENDREC.PAS                            *****
 *****                                                                   *****
 *****      Program to read binary data files created by an unformatted  *****
 *****      Fortran write (AEGIS file type of REC) and to create an      *****
 *****      unformatted UASC data file which can be read by an Alliant   *****
 *****      FX/1 or FX/8 (a BSD4.2 unix machine) or a Sun workstation    *****
 *****      after transfering the file over the ethernet with the FTP    *****
 *****      program or by using a transparent file access mechanism like *****
 *****      NFS or the V2 demo.                                          *****
 *****                                                                   *****
 *****                            Version 6                              *****
 *****                  David M. Krowitz January 20, 1987.               *****
 *****                                                                   *****
 *****      Copyright (c) 1986                                           *****
 *****      David M. Krowitz                                             *****
 *****      Massachusetts Institute of Technology                        *****
 *****      Department of Earth, Atmospheric, and Planetary Sciences     *****
 *****************************************************************************
}


PROGRAM SENDREC;


%NOLIST;
%INSERT '/sys/ins/base.ins.pas';
%INSERT '/sys/ins/streams.ins.pas';
%INSERT '/sys/ins/pgm.ins.pas';
%LIST;




CONST

{Program version number - should be same as in file header above}

    version_number = 6;


{Maximum number of bytes requested to be read into memory from the
 unformatted Fortran binary data (REC)file by the STREAM_$GET_REC routine.}

    maxsize             = 256*1024;                     {Request 256K bytes at a time}

{Maximum number of bytes in a file name}

    maxname             = 80;


TYPE

    file_name_t = packed array[1..maxname] of char;     {Input and output file name types}
    record_t = packed array[1..maxsize] of char;        {Data buffer for record}
    count_ptr_t = ^linteger;                            {Pointer to a record count}
    record_ptr_t = ^record_t;                           {Pointer to the data in a record}


        
VAR

{Definitions of input and output files}

    rec_file_name:      file_name_t;                        {Fortran unformatted (REC file) input file}
    asc_file_name:      file_name_t;                        {AEGIS ascii (UASC file) output file}
    rec_name_length:    pinteger;                           {length of REC_FILE_NAME}
    asc_name_length:    pinteger;                           {length of ASC_FILE_NAME}



{Defintions of variables for stream I/O}

    rec_ptr:            UNIV_PTR;               {pointer to first byte returned by STREAM_$GET_REC}
    rec_stream:         STREAM_$ID_T;           {stream id of input file}
    rec_attrib:         STREAM_$IR_REC_T;       {attribute block for setting AEGIS file characteristics}
    rec_err:            STREAM_$REDEF_MASK_T;   {error mask for setting AEGIS file characteristics}
    asc_ptr:            UNIV_PTR;               {pointer to first byte returned by STREAM_$GET_REC}
    asc_stream:         STREAM_$ID_T;           {stream id of output file}
    asc_attrib:         STREAM_$IR_REC_T;       {attribute block for inquiring Alliant/Sun file characteristics}
    asc_err:            STREAM_$REDEF_MASK_T;   {error mask for inquiring Alliant/Sun file characteristics}
                                                  


{Defintions of global variables}

    i,j:                INTEGER32;                  {counters}
    record_num:         INTEGER32;                  {Number of record currently being processed}
    record_len:         INTEGER32;                  {Number of bytes in the AEGIS record (without the record count)}
    record_ptr:         UNIV_PTR;                   {Pointer to the data in the AEGIS record}
    record_buffer:      record_t;                   {Buffer to hold data from AEGIS record (without the record count)}
    status:             status_$t;                  {Status returned by stream I/O calls}
    seek_key:           STREAM_$SK_T;               {Seek key for file (not used by us)}



PROCEDURE process_sequential_file;

    {Read a record and the record count from the AEGIS binary data
     (REC type) file and then write an Alliant/Sun sequential-access
     format record. Note that the record count that we are returned
     is the number of bytes of data in the record NOT INCLUDING THE
     RECORD COUNT ITSELF. The actual 32-bit integer in the AEGIS record
     counts itself as part of the record. Also note that the record
     pointer returned points to the beginning of the data in the AEGIS
     record, not to the 32-bit record count which precedes the data.}

    BEGIN

        record_num := 0;

        REPEAT
            record_num := record_num+1;

            STREAM_$GET_CONDITIONAL (rec_stream,ADDR(record_buffer),maxsize,
                             record_ptr,record_len,seek_key,status);

            IF (status.code <> STREAM_$END_OF_FILE) THEN BEGIN

                IF (status.all <> STATUS_$OK) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - unable to read next record of input file ****');
                    WRITELN ('****                  on record = ',record_num:17,'            ****');
                    PGM_$EXIT;
                END;

                IF (record_len < 0) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - unable to convert record, record is too large ****');
                    WRITELN ('****                  record = ',record_num:17,'                    ****');
                    WRITELN ('****                  # bytes   = ',record_len:17,'                 ****');
                    PGM_$EXIT;
                END;

                IF ODD(record_len) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - unable to convert record, odd number of bytes ****');
                    WRITELN ('****                  in record = ',record_num:17,'                 ****');
                    WRITELN ('****                  # bytes   = ',record_len:17,'                 ****');
                    PGM_$EXIT;
                END;


                {Apollo record seems OK, go ahead and write the Alliant/Sun sequential-
                 access format record with its two copies of the Alliant/Sun style
                 record counts.}
    
                STREAM_$PUT_CHR (asc_stream,ADDR(record_len),4,seek_key,status);
                IF (status.all <> STATUS_$OK) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - unable to write beginning record count ****');
                    WRITELN ('****                  on record = ',record_num:17,'          ****');
                    PGM_$EXIT;
                END;

                STREAM_$PUT_CHR (asc_stream,record_ptr,record_len,seek_key,status);
                IF (status.all <> STATUS_$OK) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - unable to write data to record ****');
                    WRITELN ('****                  on record = ',record_num:17,'  ****');
                    PGM_$EXIT;
                END;

                STREAM_$PUT_CHR (asc_stream,ADDR(record_len),4,seek_key,status);
                IF (status.all <> STATUS_$OK) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - unable to write ending record count ****');
                    WRITELN ('****                  on record = ',record_num:17,'       ****');
                    PGM_$EXIT;
                END;
    
                WRITELN ('');
                WRITELN ('record:     ',record_num:-1);
                WRITELN ('size:       ',record_len:-1);

            END;

        UNTIL (status.code = STREAM_$END_OF_FILE);

    END;        {End of procedure PROCESS_SEQUENTIAL_FILE.}



PROCEDURE process_direct_file;

    {Read a record and the record count from the AEGIS binary data
     (REC type) file and write an Alliant/Sun direct-access format
     record. Note that the record count that we are returned
     is the number of bytes of data in the record NOT INCLUDING THE
     RECORD COUNT ITSELF. The actual 32-bit integer in the AEGIS record
     counts itself as part of the record. Also note that the record
     pointer returned points to the beginning of the data in the AEGIS
     record, not to the 32-bit record count which precedes the data.
     Direct-access files on the Alliant/Sun do not have any record
     counts in them at all. We just need to copy the data portion of
     each of the AEGIS binary records into the output file.}

    BEGIN

        record_num := 0;

        REPEAT
            record_num := record_num+1;

            STREAM_$GET_CONDITIONAL (rec_stream,ADDR(record_buffer),maxsize,
                             record_ptr,record_len,seek_key,status);

            IF (status.code <> STREAM_$END_OF_FILE) THEN BEGIN

                IF (status.all <> STATUS_$OK) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - unable to read next record of input file ****');
                    WRITELN ('****                  on record = ',record_num:17,'            ****');
                    PGM_$EXIT;
                END;

                IF (record_len < 0) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - unable to convert record, record is too large ****');
                    WRITELN ('****                  record = ',record_num:17,'                    ****');
                    WRITELN ('****                  # bytes   = ',record_len:17,'                 ****');
                    PGM_$EXIT;
                END;

                IF ODD(record_len) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - unable to convert record, odd number of bytes ****');
                    WRITELN ('****                  in record = ',record_num:17,'                 ****');
                    WRITELN ('****                  # bytes   = ',record_len:17,'                 ****');
                    PGM_$EXIT;
                END;


                {Apollo record seems OK, go ahead and write the Alliant/Sun
                 direct-access format record without any record count.}
    
                STREAM_$PUT_CHR (asc_stream,record_ptr,record_len,seek_key,status);
                IF (status.all <> STATUS_$OK) THEN BEGIN
                    WRITELN ('**** SENDREC: Error - unable to write data to record ****');
                    WRITELN ('****                  on record = ',record_num:17,'  ****');
                    PGM_$EXIT;
                END;

                WRITELN ('');
                WRITELN ('record:     ',record_num:-1);
                WRITELN ('size:       ',record_len:-1);

            END;

        UNTIL (status.code = STREAM_$END_OF_FILE);

    END;        {End of procedure PROCESS_DIRECT_FILE.}





BEGIN

    {Type initial greetings to user.}

    WRITELN ('This is SENDREC Version ',version_number:-1,'.');
    WRITELN;


    {Get the names of the input and output files.}

    IF (PGM_$GET_ARG(1,rec_file_name,status,maxname) <> 0) THEN BEGIN
        rec_name_length := PGM_$GET_ARG(1,rec_file_name,status,maxname);
        IF (status.all <> STATUS_$OK) THEN BEGIN
            WRITELN ('**** SENDREC: Error - argument 1 exists, but bad status? ****');
            PGM_$EXIT;
        END;
    END
    ELSE BEGIN
        WRITE ('Enter name of the AEGIS format file for input: ');
        READLN (rec_file_name);
        rec_name_length := maxname;
    END;

    IF (PGM_$GET_ARG(2,asc_file_name,status,maxname) <> 0) THEN BEGIN
        asc_name_length := PGM_$GET_ARG(2,asc_file_name,status,maxname);
        IF (status.all <> STATUS_$OK) THEN BEGIN
            WRITELN ('**** SENDREC: Error - argument 2 exists, but bad status? ****');
            PGM_$EXIT;
        END;
    END
    ELSE BEGIN
        WRITE ('Enter name of the Alliant/Sun format file for output: ');
        READLN (asc_file_name);
        asc_name_length := maxname;
    END;
       

    {Open the AEGIS unformatted binary data (REC) input file.}

    STREAM_$OPEN (rec_file_name,rec_name_length,STREAM_$UPDATE,
                  STREAM_$NO_CONC_WRITE,rec_stream,status);
    IF (status.all <> STATUS_$OK) THEN BEGIN
        WRITELN ('**** SENDREC: Error - unable to open the AEGIS format binary data (REC) input file ****');
        PGM_$EXIT;
    END;


    {Create and open the Alliant/Sun format output file.}

    STREAM_$CREATE (asc_file_name,asc_name_length,STREAM_$OVERWRITE,
                    STREAM_$NO_CONC_WRITE,asc_stream,status);
    IF (status.all <> STATUS_$OK) THEN BEGIN
        WRITELN ('**** SENDREC: Error - unable to open the Alliant/Sun format data (UASC) output file ****');
        PGM_$EXIT;
    END;


    {Check if file is a sequential-access record structured file
     or if it is a direct-access record structured file. Alliant/Sun machines
     have a different file format for direct-access files (no record counts at
     all in direct-access files).}

    rec_attrib.strid := rec_stream;
    STREAM_$INQUIRE ([STREAM_$REC_TYPE],STREAM_$USE_STRID,rec_attrib,rec_err,status);
    IF (status.all <> STATUS_$OK) THEN BEGIN
        WRITELN ('**** SENDREC: Error - unable to get file-type of AEGIS input file ****');
        PGM_$EXIT;
    END;

    CASE rec_attrib.rec_type OF
        STREAM_$V1:     BEGIN
                            WRITELN ('Processing sequential-access input file.');
                            process_sequential_file;
                        END;
        STREAM_$F2:     BEGIN
                            WRITELN ('Processing direct-access input file.');
                            process_direct_file;
                        END;
        STREAM_$UNDEF:  BEGIN
                            WRITELN ('**** SENDREC: Error - file access method is undefined ****');
                        END;
    END;
        



    {We're done. Clean up and close files.}

    STREAM_$CLOSE (rec_stream,status);
    STREAM_$CLOSE (asc_stream,status);



{***** End of Program SENDREC.PAS *****}
END.


End of sendrec.pas
echo rtest.bld 1>&2
cat >rtest.bld <<'End of rtest.bld'
von
ftn rtest
bind -b rtest rtest.bin
voff
End of rtest.bld
echo rtest.ftn 1>&2
cat >rtest.ftn <<'End of rtest.ftn'
      program rtest

      parameter (isize=1000)

      dimension a(isize),b(isize),c(isize),d(isize)
      dimension a2(isize),b2(isize),c2(isize),d2(isize)
      integer*2 a,a2
      integer*4 b,b2
      real*4 c,c2
      real*8 d,d2
      character*7 odd,odd2
      character*8 even,even2

      do 123 i = 1,isize
      a(i) = i
      b(i) = (i)**2
      c(i) = float(i)
      d(i) = (float(i))**2
123   continue
      odd = 'foobar!'
      even = 'hi there'

      open (unit=1,file='test.seq.dat',form='unformatted',
     &recl=36000,status='old',access='sequential')

      read (1) a2,b2,c2,d2
      do 100 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,10) i,a(i),a2(i)
10          format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,20) i,b(i),b2(i)
20          format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,30) i,c(i),c2(i)
30          format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,40) i,d(i),d2(i)
40          format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
100   continue

      read (1) a,b,c,d
      do 200 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,210) i,a(i),a2(i)
210         format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,220) i,b(i),b2(i)
220         format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,230) i,c(i),c2(i)
230         format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,240) i,d(i),d2(i)
240         format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
200   continue

      read (1) a,b,c,d
      do 300 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,310) i,a(i),a2(i)
310         format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,320) i,b(i),b2(i)
320         format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,330) i,c(i),c2(i)
330         format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,340) i,d(i),d2(i)
340         format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
300   continue

      read (1) a,b,c,d
      do 400 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,410) i,a(i),a2(i)
410         format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,420) i,b(i),b2(i)
420         format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,430) i,c(i),c2(i)
430         format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,440) i,d(i),d2(i)
440         format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
400   continue

      read (1) b,c,d,a
      do 500 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,510) i,a(i),a2(i)
510         format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,520) i,b(i),b2(i)
520         format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,530) i,c(i),c2(i)
530         format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,540) i,d(i),d2(i)
540         format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
500   continue

      read (1) c,d,a,b
      do 600 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,610) i,a(i),a2(i)
610         format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,620) i,b(i),b2(i)
620         format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,630) i,c(i),c2(i)
630         format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,640) i,d(i),d2(i)
640         format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
600   continue

      read (1) d,a,b,c
      do 700 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,710) i,a(i),a2(i)
710         format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,720) i,b(i),b2(i)
720         format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,730) i,c(i),c2(i)
730         format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,740) i,d(i),d2(i)
740         format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
700   continue

      read (1) a,b,c,d
      do 800 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,810) i,a(i),a2(i)
810         format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,820) i,b(i),b2(i)
820         format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,830) i,c(i),c2(i)
830         format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,840) i,d(i),d2(i)
840         format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
800   continue

      read (1) a,b,c,d,a
      do 900 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,910) i,a(i),a2(i)
910         format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,920) i,b(i),b2(i)
920         format (1x,'INTEGER*9 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,930) i,c(i),c2(i)
930         format (1x,'REAL*9 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,990) i,d(i),d2(i)
990         format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
900   continue

      read (1) a,b,c,d,a,b
      do 1000 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,1010) i,a(i),a2(i)
1010        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,1020) i,b(i),b2(i)
1020        format (1x,'INTEGER*9 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,1030) i,c(i),c2(i)
1030        format (1x,'REAL*9 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,1040) i,d(i),d2(i)
1040        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
1000  continue

      read (1) a,b,c,d,a,b,c
      do 1100 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,1110) i,a(i),a2(i)
1110        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,1120) i,b(i),b2(i)
1120        format (1x,'INTEGER*9 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,1130) i,c(i),c2(i)
1130        format (1x,'REAL*9 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,1140) i,d(i),d2(i)
1140        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
1100  continue

      read (1) a,b,c,d,a,b,c,d
      do 1200 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,1210) i,a(i),a2(i)
1210        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,1220) i,b(i),b2(i)
1220        format (1x,'INTEGER*9 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,1230) i,c(i),c2(i)
1230        format (1x,'REAL*9 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,1240) i,d(i),d2(i)
1240        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
1200  continue
      close (unit=1)


      open (unit=2,file='test.dir.dat',form='unformatted',
     &recl=36000,status='old',access='direct')
         
      read (2,rec=1) a2,b2,c2,d2
      do 1300 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,1310) i,a(i),a2(i)
1310        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,1320) i,b(i),b2(i)
1320        format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,1330) i,c(i),c2(i)
1330        format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,1340) i,d(i),d2(i)
1340        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
1300  continue

      read (2,rec=2) a,b,c,d
      do 1400 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,1410) i,a(i),a2(i)
1410        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,1420) i,b(i),b2(i)
1420        format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,1430) i,c(i),c2(i)
1430        format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,1440) i,d(i),d2(i)
1440        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
1400  continue

      read (2,rec=3) a,b,c,d
      do 1500 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,1510) i,a(i),a2(i)
1510        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,1520) i,b(i),b2(i)
1520        format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,1530) i,c(i),c2(i)
1530        format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,1540) i,d(i),d2(i)
1540        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
1500  continue

      read (2,rec=4) a,b,c,d
      do 1600 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,1610) i,a(i),a2(i)
1610        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,1620) i,b(i),b2(i)
1620        format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,1630) i,c(i),c2(i)
1630        format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,1640) i,d(i),d2(i)
1640        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
1600  continue

      read (2,rec=5) b,c,d,a
      do 1700 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,1710) i,a(i),a2(i)
1710        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,1720) i,b(i),b2(i)
1720        format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,1730) i,c(i),c2(i)
1730        format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,1740) i,d(i),d2(i)
1740        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
1700  continue

      read (2,rec=6) c,d,a,b
      do 1800 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,1810) i,a(i),a2(i)
1810        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,1820) i,b(i),b2(i)
1820        format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,1830) i,c(i),c2(i)
1830        format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,1840) i,d(i),d2(i)
1840        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
1800  continue

      read (2,rec=7) d,a,b,c
      do 1900 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,1910) i,a(i),a2(i)
1910        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,1920) i,b(i),b2(i)
1920        format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,1930) i,c(i),c2(i)
1930        format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,1940) i,d(i),d2(i)
1940        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
1900  continue

      read (2,rec=8) a,b,c,d
      do 2000 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,2010) i,a(i),a2(i)
2010        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,2020) i,b(i),b2(i)
2020        format (1x,'INTEGER*4 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,2030) i,c(i),c2(i)
2030        format (1x,'REAL*4 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,2040) i,d(i),d2(i)
2040        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
2000  continue

      read (2,rec=9) a,b,c,d,a
      do 2100 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,2110) i,a(i),a2(i)
2110        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,2120) i,b(i),b2(i)
2120        format (1x,'INTEGER*9 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,2130) i,c(i),c2(i)
2130        format (1x,'REAL*9 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,2140) i,d(i),d2(i)
2140        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
2100  continue

      read (2,rec=10) a,b,c,d,a,b
      do 2200 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,2210) i,a(i),a2(i)
2210        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,2220) i,b(i),b2(i)
2220        format (1x,'INTEGER*9 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,2230) i,c(i),c2(i)
2230        format (1x,'REAL*9 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,2240) i,d(i),d2(i)
2240        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
2200  continue

      read (2,rec=11) a,b,c,d,a,b,c
      do 2300 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,2310) i,a(i),a2(i)
2310        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,2320) i,b(i),b2(i)
2320        format (1x,'INTEGER*9 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,2330) i,c(i),c2(i)
2330        format (1x,'REAL*9 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,2340) i,d(i),d2(i)
2340        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
2300  continue

      read (2,rec=12) a,b,c,d,a,b,c,d
      do 2400 i = 1,isize
        if (a(i).ne.a2(i)) then
            write (*,2410) i,a(i),a2(i)
2410        format (1x,'INTEGER*2 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (b(i).ne.b2(i)) then
            write (*,2420) i,b(i),b2(i)
2420        format (1x,'INTEGER*9 compare failed on word ',i3,/
     &              1x,'value is: ',i10,5x,'should be: ',i10,//)
        endif
        if (c(i).ne.c2(i)) then
            write (*,2430) i,c(i),c2(i)
2430        format (1x,'REAL*9 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
        if (d(i).ne.d2(i)) then
            write (*,2440) i,d(i),d2(i)
2440        format (1x,'REAL*8 compare failed on word ',i3,/
     &              1x,'value is: ',e20.17,5x,'should be: ',e20.17,//)
        endif
2400  continue
      close (unit=2)


      call exit
      end
End of rtest.ftn
echo wtest.bld 1>&2
cat >wtest.bld <<'End of wtest.bld'
von
ftn wtest
bind -b wtest wtest.bin
voff
End of wtest.bld
echo wtest.ftn 1>&2
cat >wtest.ftn <<'End of wtest.ftn'
      program wtest

      parameter (isize = 1000)

      dimension a(isize),b(isize),c(isize),d(isize)
      dimension a2(isize),b2(isize),c2(isize),d2(isize)
      integer*2 a,a2
      integer*4 b,b2
      real*4 c,c2
      real*8 d,d2
      character*7 odd,odd2
      character*8 even,even2

      do 123 i = 1,isize
      a(i) = i
      b(i) = (i)**2
      c(i) = float(i)
      d(i) = (float(i))**2
123   continue
      odd = 'foobar!'
      even = 'hi there'

      open (unit=1,file='test.seq.dat',form='unformatted',
     &recl=36000,status='unknown',access='sequential')
      write (1) a,b,c,d
      write (1) a,b,c,d
      write (1) a,b,c,d
      write (1) a,b,c,d
      write (1) b,c,d,a
      write (1) c,d,a,b
      write (1) d,a,b,c
      write (1) a,b,c,d
      write (1) a,b,c,d,a
      write (1) a,b,c,d,a,b
      write (1) a,b,c,d,a,b,c
      write (1) a,b,c,d,a,b,c,d
      close (unit=1)

      open (unit=2,file='test.dir.dat',form='unformatted',
     &recl=36000,status='unknown',access='direct')
      write (2,rec=1)  a,b,c,d
      write (2,rec=2)  a,b,c,d
      write (2,rec=3)  a,b,c,d
      write (2,rec=4)  a,b,c,d
      write (2,rec=5)  b,c,d,a
      write (2,rec=6)  c,d,a,b
      write (2,rec=7)  d,a,b,c
      write (2,rec=8)  a,b,c,d
      write (2,rec=9)  a,b,c,d,a
      write (2,rec=10) a,b,c,d,a,b
      write (2,rec=11) a,b,c,d,a,b,c
      write (2,rec=12) a,b,c,d,a,b,c,d
      close (unit=2)

      call exit
      end
End of wtest.ftn