Date: Tue, 20 Jan 87 19:13:15 est From: David Krowitz 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