You are here: Development Standards > OSAS Standards > Coding Conventions > File I/O

# File I/O

File input and output routines are often the most sensitive portions of code. Follow the guidelines below for proper error handling and multiuser control.

The term interactive program is used when the user is expected to respond to a condition in a short period of time. The term noninteractive program is used when the function proceeds to its conclusion without user involvement. Noninteractive programs need to react to some situations and errors differently or without user intervention.

The actual channel number used for each file is stored in the array variable FILE[ ], which contains 100 elements. It is created with DIM FILE[STBL(“CHANNELS”)] by the menu program. The global variable CHANNELS stores the maximum number of elements available in this array, and is set within RMSETUP. The value assigned to each file in F0 is used as the element number of the array. The actual value inside that element is the real channel number. This was created by the UNT function to provide the next available channel number. It was used by GENOPEN when that file was opened. GENOPEN then put that channel number into the appropriate array element for that file.

File access must use the FILE[ ] array and the appropriate element number.

Opening Files

The GENOPEN routine opens files. File opens are generally performed in lines 100–199. Here's an example of opening a file with GENOPEN:

`0100 LET F0=2,F$="ARCU"+STBL("CID"); GOSUB GENOPEN`

F0 contains the file reference number by which the file will be accessed with the standard file array. F$ contains the file name to open.

Closing Files

The CLOSE statement should not to be used in any application program, except

- when the file is being manipulated for a special purpose (some Resource Manager programs and the inquiry window functions do this)
- when a program must open particular files to perform initialization
- if the current task needs more than 100 files open
- as a prelude to erasing scratch files

In general, data files should not be closed. When the program is completed and the application menu is executed, a BEGIN is executed, which closes all files. This structure guarantees that later programs in a chain will also have access to the files. If you must close a file, its array element in FILE[x] must be set to zero. The preferred method of closing data files is to use the GENOPEN routine. The array element is set to zero for you when you use the GENOPEN routine to close the file.

Reading Records

The READ and READ RECORD statements are used anytime the program needs to get information out of a data file. This information cannot be changed. This task may not write back to that data file. If the information must be changed, use the EXTRACT or EXTRACT RECORD statements to get the information.

Here is an example of a READ statement:

`1100 READ (FILE[3],KEY=COMPID$,DOM=1200)IOL=OSCOMP`

Here is an example of a READ RECORD statement:

`1100 READ RECORD (FILE[3],KEY=ITEM$,DOM=1200)INVE$`

The READ RECORD and EXTRACT RECORD statements are used to read data from a file into a string template.

Use the following conventions to construct the code for the statement:

- Use the file array variable FILE[x] for the channel number.
- Define the END= clause if needed and the KEY= clause is not used (for example, when accessing the file sequentially).
- Use the ERR= statement if special processing other than SETERR GENERROR/GENLOCK is needed.
- Use the IOL= clause with a READ statement, using the appropriate line label for every read from a predefined file. If the program requires only a partial read, you can replace the IOL= clause with hard-coded variables.
- Define the DOM= clause as needed (accessing the file by key).
- Use the DIR= clause to control the movement of the key pointer. DIR=0 on a read leaves the pointer at the record read, negative values move the pointer backwards from the record read, and positive values move the pointer forward.

Writing Records

The WRITE and WRITE RECORD statements are used when the program must put a new or changed record into a data file. A record should be read and locked with the EXTRACT or EXTRACT RECORD statement if it exists before the record is written.

Here is an example of a WRITE statement:

`1100 WRITE (FILE[3])IOL=OSCOMP`

Here is an example of WRITE RECORD logic:

`1100 LET INUP$=FIELD(INUP$)`
`1110 WRITE RECORD (FILE[3])INUP$`

The assignment on line 1100 strips the trailing nulls from the string template before the template is written to the file. Failure to apply the FIELD() function to the string may result in an error 1.

Use the following conventions to construct the code for this statement:

- Use the file array variable FILE[x] for the channel number.
- Use the IOL= clause for every write from a file. Do not write out a record after a partial read.
- Use the KEY= clause only for direct files and Mkeyed files defined with a single key. The IND= clause is used for indexed files.
- The DOM= clause is used only when trying to reserve a record, as in order number control schemes.
- Precede WRITE RECORD statements with the FIELD() function to strip trailing nulls if the string template contains variable-length fields.
  Precision setting is required only when you want to force internal stored data to have a specific decimal size. The internal value is not rounded off when reading or writing to a file. Setting PRECISION before file I/O is not required or recommended. Use PRECISION only before a calculation operation where the numeric output needs to be preset.

Multiuser Issues
Locking Files

If a task is going to write large quantities of data to a file, you might want to lock the file. If no one should interfere with the write process (such as during post operations), you must lock the file. Individual record writing does not require that a file be locked.

To lock a file, send the F0 file number to the GENOPEN routine as a negative value. If F0 is a negative number, the GENOPEN routine will LOCK the file after it is opened.

The UNLOCK statement is not used in application programs. Data files that are locked upon opening are left locked throughout the program's execution. The lock is released when the data file is closed by the BEGIN in the menu program.

File locks are constraining to users. Do not use them if you can use a better method.

Locking Records

You must use the EXTRACT or EXTRACT RECORD statements when the program reserves a single record for itself to modify. You can also use the EXTRACT statement for multiple record types related to the same main record. This task requires some other manipulations.

Any file operation performed by the same user that EXTRACTed a record on the same channel will unlock that record. Some files contain multiple record types. This usually requires a series of reads to access all the information for a single set of data. If you must access more than one record in the same file, be careful not to lose your record lock.

Below are some strategies for locking multiple records:

Plan 1 | EXTRACT the main record on one channel. OPEN the same file on another channel number. Perform the balance of the file operations with the secondary channel number. All programs in all applications must use the same locking scheme for each particular file and record type
Plan 2 | READ record type 1. READ record type 2. EXTRACT record type 1. This strategy leaves a small window of vulnerability between the first READ and the EXTRACT, because another user can get in and read the same record for another task and then find that the data has been changed. To make sure that this scheme works and to keep vulnerability to a minimum, put the READs and the EXTRACT tight together in the code. Tasks that need to access the same record combination must use the same order of access. This means that if two users are in the same function in the same record, one will be denied access, because one will have the EXTRACT first.
Plan 3 | Create a temporary record somewhere and lock it. Perform all file processing. Remove that temporary record when done. All programs in all applications must follow the same scheme for each particular file and record type.