! 21. Using Files.					File: files21.f90
!------------------------------------------------------------------------------!

MODULE files
	IMPLICIT NONE
CONTAINS

	SUBROUTINE introduction()
		IMPLICIT NONE

		! 0. Introduction.

		PRINT *
		PRINT *,'		USING FILES	'
		PRINT *
		PRINT *,'This session expands on the possibilities allowed by &
			&Fortran to read input from files, write output to &
			&files, and append output to existing files. The ideas &
			&are introduced in the following very artificial, &
			&thickly commented program. Run the program and &
			&examine carefully the output files from each part &
			&before proceeding to the next.'


	END SUBROUTINE introduction

!------------------------------------------------------------------------------!

	SUBROUTINE proceed(item)
		IMPLICIT NONE

		INTEGER, INTENT(OUT) :: item

		! decide which item you wish to examine. 
		PRINT *
		PRINT *,'Which item do you wish to proceed with?'
		PRINT *
		PRINT *,'1. To OPEN and CLOSE a file: type 1'
		PRINT *,'2. Records, formatted files, sequential files : type 2'
		PRINT *,'3. Open file specifiers : type 3'
		PRINT *,'4. Checking file status : type 4'
		PRINT *,'5. File positioning : type 5'
		PRINT *,'6. A warning! : type 6'
		PRINT *,'To QUIT type any other number'
		PRINT *
		READ *, item
		PRINT *

		IF (item > 6 .OR. item <= 0) STOP

	END SUBROUTINE proceed

!------------------------------------------------------------------------------!

	SUBROUTINE open_close(item)
		IMPLICIT NONE

		! 1. Opening and closing files.

		INTEGER, INTENT(IN) :: item


!! 1a.	A summary of what we have used so far. To write output to a file we
!!	have used the system's default facilities which automatically connects
!!	the UNIT=n to the file 'fort.n' (n, some integer, but not 5 or 6). 
!!	For example,

		WRITE(11,*) 'Hi, fellow physicists'

!!	connects the file 'fort.11' to the UNIT=11
!!
!! 1b.	The above could have been written more fully as

		OPEN ( UNIT=12, FILE='fort.12' )
		WRITE (12,*) 'Hi, spectroscopists'
		CLOSE(12)

!!    Therefore, to select an output filename of your own choice, say 
!!    'myfile', you could write

		OPEN ( UNIT=13, FILE='myfile' )
		WRITE (13,*) 'Hi, theorists'
		CLOSE(13)

!!    The OPEN statement connects a file called 'myfile' as UNIT 13. The 
!!    output is then written to it and finally the UNIT=13 is CLOSEd (i.e.
!!    the communication channel is disconnected). If the CLOSE statement is
!!    omitted then the UNIT is automatically disconnected when the program 
!!    finishes its execution. 

!!    This is probably as much as we need to know to handle most of our 
!!    simplest needs. However, read on for more sophistication.

	END SUBROUTINE open_close

!------------------------------------------------------------------------------!

	SUBROUTINE file_format(item)
		IMPLICIT NONE

		! 2. Records and formatted/unformatted files.

		INTEGER, INTENT(IN) :: item
		INTEGER :: count=134

!! 2a.	Files and records.
!!
!!	Fortran views files as consisting of RECORDS, which for our purposes
!!	may be treated as lines of output. One record is one line of output.
!!
!!	There are three types of record in Fortran - formatted, unformatted 
!!	and endfile records.
!!
!!	A sequence of records forms a file. There are two types of file - 
!!	sequential-access (like records stored on a tape) and direct-access
!!	(as might be possible with a magnetic disk).

!! 2b.	Formatted versus unformatted files.
!!
!!	In all of the files we have written so far, characters (i.e. symbols 
!!	which are intelligible to us as opposed to intelligible only to the
!!	system) are sent to the files. Such files are said to be "formatted".
!!	In fact, it is the character codes (e.g. the ASCII codes) for these
!!	characters which are stored.

		OPEN( UNIT=24, FILE='yourfile' )
		WRITE(24,201) count
		201 FORMAT(1X,I3)
		CLOSE(24)

!!	The above program requires a format specifier (which could have been *)
!!	to indicate how the character codes for '1' then '3' then '4' were to
!!	be stored in the file 'yourfile'. Such a file is formatted.
!!
!!	Consider the following program:

		OPEN( UNIT=25, FILE='hisfile', FORM='UNFORMATTED' )
		WRITE(25) count
		CLOSE(35)

!!	In this case there is no format specifier. The WRITE statement sends 
!!	the internal binary bit patterns to the file - unreadable to us 
!!	(viewing this file will show garbage or nothing at all). So formatted 
!!	files and unformatted files are fundamentally different. The 
!!	conversions from the internal bit patterns to the character codes 
!!	which allow us to read a file impose a considerable burden in terms of 
!!	speed of execution and storage required. If data is only being output 
!!	from one program to be used as input for another, then there is no 
!!	need for the conversions to take place. If programs are to be 
!!	intelligible to us, then they must be formatted files. We will not 
!!	consider unformatted files further. If the FORM= specifier is omitted 
!!	then the file is assumed to be formatted by default.

!! 2c.	Sequential access files.
!!
!!	A sequential access file has its records written in sequence as would
!!	occur on a magnetic tape. At the end of such a file, a special record,
!!	the "end-of-file record" (EOF) is written. Sequential access files are
!!	always read from the beginning until we reach the record of interest. 
!!	To read the whole file, all records are read sequentially until the 
!!	endfile record is reached. Clearly this is an inefficient process if 
!!	we only want to read a few records deep within a large file. Their 
!!	advantage is that they are conceptually easy to understand and handle 
!!	within a program.

!! 2d.	Direct-access files
!!
!!	Fortran provides an alternative form of storage in the form of a file
!!	in which it is possible to hop around from record to record directly.
!!	A direct-access file can have its records written or read in random 
!!	order. A magnetic disk is a good conceptual model of such a file, 
!!	where any location on the disk can be accessed directly (without 
!!	having to pass over all the other records as happens on a tape). 
!!	In order to hop around in such files, Fortran must know where each 
!!	record is on disk. In order to do this, each record must be of the 
!!	same length and the length must be specified when OPENing the file. 
!!	While the length of a formatted record is just the number of characters 
!!	in it, the length of an unformatted record is machine dependent. 
!!	Magnetic disks ofcourse can store both direct-access files and 
!!	sequential files. Usually we do not need to worry about the physical 
!!	storage device, only about the type of file.
!!	We will not consider direct-access files further. The default format
!!	is a sequential file.

	END SUBROUTINE file_format

!------------------------------------------------------------------------------!

	SUBROUTINE open_file(item)
		IMPLICIT NONE

		! 3. Opening files.

		INTEGER, INTENT(IN) :: item
		INTEGER :: ios

!! 3a.	OPENing files.
!!
!!	We have seen in the examples above how files are connected to a unit
!!	number. The full syntax allows for a variety of options:
!!
!!		OPEN ( olist )
!!
!!     where the "olist" is a list of open specifiers:
!!
!!		UNIT = unit_number	 	(positive integer)
!!		FILE = filename			(default is 'fort.u')
!!		STATUS = 'OLD','NEW','REPLACE','UNKNOWN', or 'SCRATCH'
!!						(default is 'unknown')
!!		FORM = 'FORMATTED' or 'UNFORMATTED'
!!						(default is 'formatted' for 
!!						 sequential files;
!!					 	 'unformatted' for direct 
!!						 access files)
!!		ACCESS = 'SEQUENTIAL' or 'DIRECT'
!!						(default is 'sequential')
!!		ACTION = 'READ','WRITE','READWRITE'
!!						(default is READWRITE usually)
!!		POSITION = 'REWIND','APPEND','ASIS'
!!						(default is ASIS usually) 
!!		IOSTAT = ios			(integer)

!! 3b.	The UNIT = u is the only clause always required. If no file is named
!!	then system connects to 'fort.u' by default. The UNIT = u may be 
!!	written simply as  "u"  provided it appears first in the olist.

!! 3c.	The FILE = filename identifies a file called 'filename', a character 
!!	expression.

!! 3d.	The STATUS clause indicates whether the file we wish to connect to
!!	already exists (OLD), is to be created (NEW), is to added to (APPEND) 
!!	or that we do not care if it exists or not (UNKNOWN). If SCRATCH is 
!!	specified, the system sets up a temporary file for use during a run 
!!	to store intermediate values. It is deleted at the end of the run.
!!

!! 3e.	The FORM indicates whether the file is a FORMATTED or UNFORMATTED file.

!! 3f.	The ACCESS indicates whether the file is a SEQUENTIAL or DIRECT-ACCESS 
!!	file.

!! 3g.	ACTION indicates whether the file can only be read (READ), written 
!!	to (WRITE), or either (READWRITE).

!! 3h.	POSITION allows a position other than the beginning to be specified.

!! 3i.	The IOSTAT=ios is a means of identifying whether an error has occurred 
!!	during the connection process. It stores a number called "ios" (or a 
!!	name of your choice) indicating the outcome of executing the OPEN 
!!	statement.
!!	A value 0 indicates success.
!!	On most occasions we will use the defaults.

!! 3j.	Examples.

		OPEN  ( 34, FILE='phys1' )
		WRITE ( 34,* ) 'Gday, mates'
		CLOSE ( 34 )

		OPEN ( UNIT=35,FILE='phys2', &
			&STATUS='UNKNOWN', FORM='FORMATTED',&
			&ACCESS='SEQUENTIAL', ACTION='READWRITE', &
			&POSITION='ASIS', IOSTAT=ios )
		IF (ios /= 0) THEN
			PRINT *,'An error has occurred during opening of phys2'
		END IF
		WRITE ( 35,* ) 'Goodnight, sweetie'
		CLOSE ( 35 )

!!    Both of the above pieces of code achieve the same effect: they attempt to
!!    open a sequential, formatted file called phys1/2 and connect it to unit
!!    34/35. The status is unknown indicating that the file is to be opened 
!!    whether it exists or not. The IOSTAT clause is included in the second
!!    case to tell us if the system cannot carry out the command (the file name
!!    may be illegal, or the unit may already be connected to another file).

	END SUBROUTINE open_file

!------------------------------------------------------------------------------!

	SUBROUTINE file_check(item)
		IMPLICIT NONE

		! 4. Checking file status and reporting errors.

		INTEGER, INTENT(IN) :: item
		INTEGER :: ioerror
		CHARACTER(LEN=20) :: old_file, new_file, yes*1

		! checking that an old file exists
		PRINT *, 'Name of file to read?'
		READ (*,41) old_file
		41 FORMAT(A20)

		OPEN ( UNIT=42, FILE=old_file, STATUS='OLD', IOSTAT=ioerror )
		! error trap
		IF (ioerror /= 0) THEN
			WRITE(*,411) '++Cannot open file  ',old_file, '  ++',& 
			&'  Error code = ', ioerror
			411 FORMAT (/1X, 3A/ 1X, A, I8/)
			STOP
		END IF

		PRINT *, 'File is open'

		! insert code to read contents of file here etc, then close

		CLOSE (42)

!! 4a.	If the filename you input does not exist, then an error is reported and
!!	execution stops.
!!	Note that the above does NOT read the contents of the file, merely 
!!	reads the character variable 'oldfile' if it is present.

		! ready to continue?
		PRINT *
		PRINT *,'Are you ready to continue: if yes type y'
		PRINT *
		READ *, yes

		! creating a new file and aborting if the file already exists
		PRINT*, 'Name of new file to create?'
		READ (*,43) new_file
		43 FORMAT(A20)

		OPEN ( UNIT=44, FILE=new_file, STATUS='NEW', &
			&IOSTAT=ioerror )
		! error trap
		IF (ioerror /= 0) THEN
			WRITE(*,45) '++Error while opening new file+++'& 
			&'  Error code = ', ioerror
			45 FORMAT (/1X, A/ 1X, A, I8/)
			STOP
		END IF

		PRINT *, 'File is open'

		! now you can read/write etc to file. Note that it now 
		! becomes OLD.

		CLOSE (44)


!!     here the aim was to be sure that the file did not already exist. If it
!!     did then it could be overwritten. The above code, by declaring the file
!!     to be 'NEW' and generating an error message if the file already exists,
!!     prevents this.
!!
	END SUBROUTINE file_check

!------------------------------------------------------------------------------!

	SUBROUTINE file_position(item)
		IMPLICIT NONE

		! 5. Positioning within files.

		INTEGER, INTENT(IN) :: item
		INTEGER :: i, ios
		INTEGER, DIMENSION(17) :: int
		CHARACTER(LEN=20) :: old_file, new_file, yes*1



!! 5a.	Endfile records.
!!
!!	Most Fortran systems append an endfile record at the end of a 
!!	sequential file when the file is CLOSEd. Such a record can be 
!!	attached by the ENDFILE statement:
!!
!!			ENDFILE u
!!
!!	where u is the unit to which the file is connected. This puts an 
!!	endfile record at the current position in the file, leaving the file 
!!	positioned immediately after the endfile record.

!! 5b.	File positioning.
!!
!!	We are now in a position to open and close a file, read records from 
!!	it or write records to it. Well, almost. We also would want to add
!!	records to an already existing file, without overwriting the data that
!!	is already contained in it. To this end, we need to be able to position
!!	the file. It is helpful to imagine a pointer which indicates the 
!!	position in the file. When a file is first opened, the pointer is 
!!	placed before the first record. If a whole file is read, the pointer 
!!	will finish after the endfile record. Otherwise it will be positioned 
!!	before some record within the file.
!!
!!	Since no data can occur after the endfile record, in order to read 
!!	data from, write or append data to the file, the pointer needs to 
!!	be repositioned. The following two statements enable this:
!!
!!			BACKSPACE  u
!!
!!	where  u  is the unit number to which the file is attached. It causes 
!!	the pointer to be positioned just before the preceding record. If the 
!!	pointer is currently positioned just after the endfile record, this
!!	will reposition it just before it and hence allow new data to be 
!!	appended to the file.
!!
!!			REWIND  u
!!
!!	This causes the pointer to be positioned before the first record of the
!!	file. Then data can again be read from the beginning of the file.  
!!
!!	Remember that the pointer remains where it was last positioned. When a
!!	file is first connected (OPENed) the pointer appears before the first
!!	record.
!!
!!	If the pointer is positioned before a particular record and new data
!!	is written to that file, it overwrites previous records wiping all 
!!	records from that position until the end including the endfile record. 
!!	A new endfile record then needs to be added (if not already done by 
!!	the system automatically when closing the connection to the file).
!!
!!	Examples:

		! open a new file 'myfile.data' and write data into it.

		OPEN ( 51, FILE='myfile.data' )

		DO  i=1,20,2
			WRITE(51,*) i
		END DO

!! on each cycle through the loop a record has been added to the file, the 
!! pointer being positioned immediately after that record.

		! ready to continue?
		PRINT *
		PRINT *,'Are you ready to continue: if yes type y'
		PRINT *
		READ *, yes

		! add a further set of data to 'myfile.data'
		DO i=21,30,3
			WRITE(51,*) i
		END DO

		ENDFILE 51

!! a further set of data is appended to the file and an EOF record added. 
!! The pointer is located immediately after the EOF record.

		! ready to continue?
		PRINT *
		PRINT *,'Are you ready to continue: if yes type y'
		PRINT *
		READ *, yes

		! position the pointer just before the endfile record 
		! and append new data.

		BACKSPACE 51

		DO  i=31,40,4
			WRITE(51,*) i
		END DO

		ENDFILE 51


		! read data from 'myfile.data' into the array 'int' 
		! and print it to screen

		REWIND 51

		PRINT *,'read data from myfile.data'
		READ(51,*) int
		PRINT *, int
	
		CLOSE(51)

!! the above positions the pointer at the beginning of the file, reads the
!! whole file into the array and then prints out the array, finally closing
!! the connection.

		! ready to continue?
		PRINT *
		PRINT *,'Are you ready to continue: if yes type y'
		PRINT *
		READ *, yes

		! open a channel to myfile.data and position pointer at end 
		! of file ready for new data to be added

		OPEN ( 52, FILE='myfile.data', POSITION='APPEND' )

		! file now positioned ready for appending new data
		! now add new data to file myfile.data

		DO i=41,50,5
			WRITE(52,*) I
		END DO

		ENDFILE 52
		CLOSE(52)

	END SUBROUTINE file_position

!------------------------------------------------------------------------------!

	SUBROUTINE warning(item)
		IMPLICIT NONE

		! 6. Warning against losing/overwriting data.

		INTEGER, INTENT(IN) :: item
		INTEGER :: i
		INTEGER, DIMENSION(19) :: int

!! 6a.	Warnings.
!!
!!	Be aware of the defaults. There is safety in being explicit at all 
!!	times and it is recommended. In practice, however, the defaults are 
!!	often assumed.
!!
!!	Be very careful when updating an already existing file. It is very easy
!!	to make simple errors which corrupt files and hence lose data. A good
!!	practice is always to back up files before trying to modify them. One
!!	of Murphy's laws says that you can never have too much backup!!
!!	Consider the following example. You will first need to create a file 
!!	called 'original.data' containing 19 integers.

		! make a backup
		OPEN (60, FILE='original.data')

		DO i=1,19
			READ(60,*) int(i)

		END DO

		CLOSE (60)

		! now copy array to a new file
		OPEN (61, FILE='newfile.data')
		DO i=1,19
			WRITE(61,*) int(i)
		END DO

		! now we can safely add new data to the new file

		BACKSPACE 61

		DO i=51,71,6
			WRITE(61,*) i
		END DO

		ENDFILE 61
		CLOSE(41)

	END SUBROUTINE warning

!------------------------------------------------------------------------------!

END MODULE files

!------------------------------------------------------------------------------!


PROGRAM file
	USE files
	IMPLICIT NONE

	! The following session is in the form of a longish very artificial 
	! program illustrating the many powerful input/output features which 
	! Fortran supports. Each small section introducing a new idea is 
	! heavily commented (the !! lines) at the point of introduction. 
	! View this program and at the same time execute it to see the action 
	! performed by each part. 

	INTEGER :: item, index

	CALL introduction()

	! decide on next section to study
	CALL proceed(item)

	DO index=1,20

		SELECT CASE(item)
		CASE (1)
			CALL open_close(item)
			CALL proceed(item)
		CASE (2)
			CALL file_format(item)
			CALL proceed(item)
		CASE (3)
			CALL open_file(item)
			CALL proceed(item)
		CASE (4)
			CALL file_check(item)
			CALL proceed(item)
		CASE (5)
			CALL file_position(item)
			CALL proceed(item)
		CASE (6)
			CALL warning(item)
			CALL proceed(item)
		CASE DEFAULT
			CALL proceed(item)
		END SELECT
	END DO

END PROGRAM file

!! 7.	Read chapters 8 and 9 of Ellis.

!!	END OF FILE: files21.f90
