Chapter 14

From BR Wiki
Jump to navigation Jump to search

Now that you’ve had an introduction to the world of file processing, we’re going to dive right into the use of internal files. When you finish this chapter you will be able to:

  • Use and design a record layout.
  • Create a new internal file using the OPEN statement.
  • OPEN an existing internal file.
  • Use the six I/O statements (READ, REREAD, WRITE, REWRITE, RESTORE and DELETE) for internal file processing in your programs.

It is important for you to know about the overlap of this chapter with the next two chapters. All three give you information about the important topic of processing internal files. In many cases, the information that you learn later can also be applied to the information that you learned earlier -- so it is a very good idea to try to cover all three chapters in a short period of time, then come back and practice what you have learned.

14.1 Determining the record layout

The very first step involved in creating an internal file is determining the layout of the records that the file is to hold. This may seem like a fairly simple task, but it is also a very important task. The programmer must make accurate judgments about the types and lengths of fields that a record is to hold, because inaccurate estimates can cause operational difficulties when the file is being used or extended. Also, changing the record layout once a file has been filled with records can be a time-consuming and troublesome process.

The following is an example of a record layout sheet for a file of information about checking account activity. It lists all the fields in a single record and tells where each field is found within that record. Throughout the remainder of the file processing section of this tutorial, this record layout will be used for a file called CHEKBOOK.INT.

Field Description Form Positions
2 AMOUNT N 10.2 6-15
6 PAYEE C 25 29-52

According to the above layout, each record in CHECKBOOK.INT file is 62 characters long and contains a total of seven fields.

The SEQUENTIAL method of access with INTERNAL files

Each of the seven fields (check number, amount, deposit/withdrawal flag, etc.) is described in the first column in the layout sheet. The second column describes format of the fields; if you think back to the chapter that discussed formatted printing and use of the FORM statement, you should recognize the meanings of the figures in this column. The third column lists the record positions that the fields will occupy. Notice that there are no extra spaces between fields; since internal files cannot be seen by people anyway, there is no reason to make them readable by adding spaces between fields.

To take the first field in the above layout as an example, the information that you should be able to determine from this entry is that check number is a numeric field which is 5 digits long and located in the first 5 bytes of the record.

You might note that the third field is only one byte long. Can anything important be stored in a one-byte field? You betcha! This little guy contains a D if the record is a deposit and a W if it is a withdrawal. This one-byte indicator, sometimes called a flag, is stored in position 16. In future references to the CHEKBOOK.INT file, we will assume that when there is a W in position 16 but no check number, the transaction that occurred involved a withdrawal that was not a check (perhaps it was a bank service charge or a cash machine withdrawal).

You might also note that the descriptions of the fourth and fifth fields indicate that the date will be stored in the format YYMMDD (year first, then month, then day, with no slashes, hyphens or spaces as separators). As the “Sorting Dates” box describes, this special date format makes the process of sorting information by date a quick and easy process.

Sorting Dates

One of the ways that program users like to have information sorted is by date, and Business Rules! can do this quickly and easily when the date is stored as a six-digit number beginning with the year.

When the date is stored as one six-digit number (rather than as three two-digit numbers), Business Rules! can handle the whole sorting process with just one sort specification rather than with three. The trick to this one-specification process, however, is that the year has to occur first in the number. If this doesn’t happen, the number representing some checks that were written at an earlier date will have a higher value than some written at a later date, and the sort won’t be accurate.

As an example consider two checks, one written on 12-31-86, and the other written on 1-10-87.

The first would be stored as 123186 when N 6 is the conversion specification, and the second would be stored as 011087. If these two dates were sorted from lowest to highest, the sort would identify the 1986 check as having been written after the 1987 check. Put the year first though, and 870110 will come out higher than 861231 every time.

Most programmers don’t like the idea of asking operators to enter dates in the YYMMDD format. The way to get around this is to have the operator enter in MMDDYY format and use a combination of substring and concatenation operations to change the order of the date before the number is stored.

By the way, this is also the reason that the system variable DATE$ returns an 8-character string in YYMMDD format.

Quick Quiz 14.1

1) Number each of the following field descriptions according to their order in the CHEKBOOK.INT record layout:

a) The amount of the check.
b) The person or company to whom the check was written.
c) The withdrawal/deposit indicator.
d) The date the check was written.
e) The number of the check.
f) The date the check cleared the bank.
g) The account number of the person writing the check.

2) Which of the following is often the best format for storing dates in an internal file?


3.) What is the largest check amount that can be represented in the second field of the CHEKBOOK.INT file?

a) $9,999,999.99
b) $9,999,999,999.99
c) $99,999.99

4) If the CHEKBOOK.INT file contained 8000 records, approximately how much space would the entire file take up?

a) 8000 bytes.
b) 440,000 bytes.
c) Impossible to estimate.

NOTE: There are three correct answers for each of the remaining questions.

5) The first field in the CHEKBOOK.INT file:

a) Is an internal file.
b) Is 6 bytes long.
c) Starts in position 1.
d) Ends in position 5.
e) Is a numeric field.
f) Is a string field.

6) The second field in the CHEKBOOK.INT file:

a) Is an external file.
b) Is 10 bytes long.
c) Starts in position 6.
d) Ends in position 16.
e) Includes a decimal point.
f) Is a string field.

7) The sixth field in the CHEKBOOK.INT file:

a) Is an internal field.
b) Is 25 bytes long.
c) Starts in position 6.
d) Ends in position 53.
e) Is a numeric field.
f) Is a string field.

Answers 14.1

14.2 Suggestions for determining field lengths

When a single record is divided into fields, it is important to remember that the size of the field is predetermined and limited by the programmer. Let’s take the information in the CHEKBOOK.INT file as an example.

If the field for the payee’s name is 25 bytes long (meaning that it will allow for a total of no more than 25 letters, punctuation marks and spaces), then all payee names will have to fit in this space. If a payee’s name is longer than this limit, the program operator will have to abbreviate it or else let the program truncate (chop the end off) it. As you will learn later, the SOFLOW error condition can be used to catch this situation. If the payee’s name is shorter than 25 keystrokes, spaces will fill the last few positions.

Another field in CHEKBOOK.INT file is a 6-digit field (in YYMMDD format) for “date-cleared” the date the check clears the bank. For a check that clears on October 22, 1987, this field could contain the 6-digit number 871022. If someone tried to put an 8-digit number in this field, the program would probably generate an error. (The CONV error condition can be used to trap this situation.) If no error occurred, there would be trouble because the extra digits would spill over into some other field and invalidate (or “clobber”) its data.

By now you may be thinking that it’s a good idea to overestimate the lengths of fields and to always provide a few extra bytes. Sometimes this is true; adding a few extra bytes can be a sensible and necessary planning tool for the future. As an example, imagine that you are creating a file that keeps track of product inventory for an auto parts company. If the company currently has parts with identifying numbers that are up to 9000 and you know they will be over 10000 within a year, you should build this fact into the record design by allowing for a 5-byte field. (Besides, identifying numbers probably won’t go past 99999 for at least another 10 years.)

So, yes, it can be a good idea to “pad” your fields with extra bytes. But you should also keep in mind that unnecessary extra bytes on several fields can add up to a lot of wasted space. In a product inventory file of 10,000 records, two extra bytes on each of five fields would be 2 x 5 x 10,000 = 100,000 extra bytes!

Using the OPEN statement to create a new file.

As you may remember from the last chapter, the OPEN statement must be used whenever you wish to access a data file. The OPEN statement must also be used when you wish to create a data file.

Besides the line number and the OPEN keyword, the syntax of the OPEN statement contains five major parts: the file number, the file identification string, the file type, and the type of use and the method of access. Keep in mind that, while the name and type of a file must remain constant, the same internal file can be opened for all different types of uses and methods of access. The following OPEN statement creates the file CHEKBOOK.INT:

500 open #5: ", recl=63,new", internal, output, sequential  

The parts are: line number, OPEN, file number, file ID string, file type, method of access.

While the length of this statement may make it look a bit intimidating, it really can be broken into some very logical and easy to understand parts. Each of the following sections describes one of these parts in detail.

File number

The file number is an arbitrary number from 1 to 127; it serves as a shortcut, or “nickname” for the file you are creating. Later in the program, instead of having to refer to CHEKBOOK.INT, you can just refer to #5.

File identification string

The Name=“CHEKBOOK.INT,RECL=63” portion of the sample OPEN statement is the file identification string. This particular string contains three parts because this OPEN statement is being used to create a file. (When an OPEN statements is used to access a file that has already been created, the RECL= portions must be omitted.) The entire file identification string must always be enclosed within quotation marks or can be a string expression; letters may be uppercase, lowercase, or a mix of both.

The three portions of this file identification string are as follows:

1. NAME= - The five characters NAME= must be specified in the file identification string. After the equal sign, any valid filename may appear. Drive letter and full pathname are optional. This part of the OPEN statement is the only place in the entire program that the true name of the file needs to be referred to. Until the file is closed, Business Rules! will allow you to use the file number (#5, in our example) to refer to the file whenever necessary.

2. RECL= - This second portion of the file identification string specifies the record length of the file to be created. It must be included in the string when a file is being created only.

Notice that our sample OPEN statement creates CHEKBOOOK.INT with an initial size of zero bytes. The reason for this is that Business Rules! will automatically increase the file size as space is needed (this is called dynamic extension). To do this, the formula (RECL+1) * (expected # of records) is used.

3. NEW - NEW indicates that the file to be opened must also be newly created. In the place of NEW, there are two other options: NEW or REPLACE.The USE parameter indicates that an existing file by the specified name should be opened. If a file by the specified name does not exist, Business Rules will create a new file for it.

REPLACE indicates that any existing file under the specified name should be freed and that a new file under that same name be created. There will be no warning that the file is being replaced when this parameter is used.

File type

The “INTERNAL” portion of the sample OPEN statement indicates the file type. There are three possible keywords that can appear here: INTERNAL, DISPLAY or EXTERNAL.

Method of access

The “OUTPUT” portion of the sample OPEN statement indicates the method of access for the file. Since it is desirable for the records in this file to be placed one after the other, it has been opened for sequential access. The other methods of access that could be specified for internal files are RELATIVE and KEYED.

Quick Quiz 14.2

True or False:

1. The RECL= clauses are part of the file identification string.

2. RECL=0 is a good choice for new files.

1. True
2. False

14.3 Building the CHEKBOOK.INT file

Now that you’ve learned how to create the internal file, you can begin filling it with data. The Business Rules! statement that adds records to internal files is the WRITE statement. The first rule to keep in mind about the WRITE statement is that the file it is adding information to must have already been opened with the OPEN statement. The OPEN statement that we used as our syntax example in the last lesson will work perfectly for our purposes (it is reprinted below). Type this statement into Business Rules!:


Now type in the following two lines:

600 WRITE #5,USING 700: 1044,106.45,”W”,871104,871112,”SAXO MUSIC”,10225340
700 FORM N 5,N 10.2,C 1,N 6,N 6,C 25,N 8

Go ahead and RUN this program. It will take only a few seconds and nothing much will seem to happen, but by the time the READY prompt shows in the bottom of your screen again your program will have created the CHEKBOOK.INT file and placed one record in it.

How did all this work, you ask? Well, you should already be familiar with the OPEN statement in line 500. It told Business Rules! to create the file. And the WRITE statement in line 600 worked in conjunction with the FORM statement in line 700 to send a record to the file.

If you look more closely at the WRITE statement, you’ll see that there is a colon just after the USING clause. The list of information after the colon represents the record that was added to the CHEKBOOK.INT file; each of the items corresponds to the fields in the CHEKBOOK.INT record layout.

The information before the colon indicates where the record should be written (to #5, the nickname that the OPEN statement had just assigned to CHECKBOOK.INT). The USING clause in this half of the statement tells Business Rules! where to find the FORM statement that describes the format of the information to be written. The FORM statement in line 700 works in the same manner that it did when you used it with formatted printing. Note that each of the format identifiers in this statement follows exactly what was planned in the record layout.

As you learn about other data-transferring I/O statements, you’ll find that they also follow the same general syntax as the WRITE statement. In each case, the colon acts as a major separator within the statement: the information after the colon indicates what is to be transferred, and the information before the colon explains how it is to be transferred.

Using the REPLACE parameter in the OPEN statement

Now, without making any changes to the program RUN it again. You should get an error. Look the error number up and see if you can figure out what happened.

Error 4150 (duplicate file name) will occur. The reason that you got this error is that your OPEN statement in line 500 told Business Rules! to create a file that already existed (because this same program had created it just a few minutes earlier). Since the Business Rules! system does not want you to accidentally lose the contents of the existing file, it sends you a warning message in the form of an error. This error can save you from lots of trouble at times, but there will be other times when you’ll want to get rid of an old file by replacing it with an entirely new file.

Business Rules! lets you take the risk of replacing an old file into your own hands by allowing you to code the keyword REPLACE in the file identification string of the OPEN statement.

Doing this tells the system to replace any file that exists with the same name as the one you are creating. LIST your program and change the OPEN statement in line 500 as follows to do this:


Now you can run your new program as many times as you like.

Adding more records to CHEKBOOK.INT

You could continue to add records to the CHEKBOOK.INT file by entering more WRITE statements that contain the exact data that needs to be entered. Each one would use the FORM statement in line 700 for the formatting.

But the difficulties of building a file in this manner are extensive. First, it requires a programmer instead of a data entry operator. Most data files require the ongoing addition of records, and this is usually handled by a data entry professional who is quick and accurate. Second, it is slow, and third, it wastes a lot of program space.

A much better way of handling the output of information from a program to a data file is to make it easy for somebody else to enter the information. As an example, look at the following program. It provides the operator with a screen (using full screen processing) that asks for all the information needed for a record: check number, amount, type of transaction, date written, date cleared, and payee and account number. It assigns this list of information to a set of variables labeled, and then uses a WRITE statement to send the whole set of variables to the CHEKBOOK.INT file.

You should use this program to enter information from your own checkbook into the CHEKBOOK.INT file. Its available with your supplemental programs.

Mid-Chapter Practice

The program CHEKBOOK.BRS uses a list of variables for each piece of information input by the user. This works great for simple programs, but with larger and more complicated projects, a better way to handle input from the user is to assign it all to an array.

List the program and find line 190:

00190 INPUT FIELDS MAT inpdef$: chnum, amount, typetran$, datew, datec,


Notice that the variables include both numbers and strings. The simplest way to combine both kinds of variables is to write two arrays, ENTRIES and ENTRIES$ and assign the values according to their place in line in the form statement, using POS.

Your challenge is to re-write the appropriate lines (don’t forget to DIM these new arrays as well) so that the input is stored in these arrays.

NOTE: There is a tool that can help you do this faster. In chapter 15, we describe a program called FileIO which can write parts of this code for you, once your program has been edited to work with it. It’s available online along with instructions.


Quick Quiz 14.3

1. True or False: When you create a new file, coding the REPLACE parameter at the end of the file identification string will override an error message when the file already exists.

2. The purpose of the WRITE statement is:

a) To add new records to a file.
b) To change existing records in a file.
c) To generate handwriting on the screen.

3. When a WRITE statement does not contain enough information for all the fields in the record, the positions in the record which are not specified in the FORM statement:

a) Will be written as blanks to the record.
b) Will contain any characters which may have been left on the disk by a previous file in that location.

4. With files opened for sequential processing, the WRITE statement can only add records after:

a) The first record in the file.
b) The last record in the file.
c) The last record read.
d) The next record pointed to by the file pointer.

5. Data-items can include:

a) Everything that var-names can include.
b) String and numeric constants.
c) Arithmetic expressions like 5*2.
d) String and numeric functions.
e) All of the above.

Answers 14.3:

14.4 Using the OPEN statement for an existing file.

At this point, CHEKBOOK.INT should be a data file. What can you do with a file such as this? Use it in program processing!

Imagine that you are the official programmer for Jack’s Country Bank, and that Jack would like to find out just how much money the bank is sending out each day and to whom. He would like to be able to print out a report of the amounts and recipients for all checks written. Using a combination of PRINT and READ statements, you can write a program that does just this.

The first major step for a program that accomplishes this task is to open the internal file CHEKBOOK.INT. The OPEN statement for an existing file is very similar to the OPEN statement for creating a file except that you cannot include the RECL= or REPLACE parameters in the file identification string. If you did include RECL=, Business Rules! would attempt to create a new file with the name you specified and, finding that a file by that name already exists, it would send you an error message. If you also included the REPLACE parameter, Business Rules! would assume that you intended to replace the old file with a new one; it would automatically replace the contents of the file you specified with an empty file and all your data for that file and hard work would be lost.

Type the following OPEN statement for the existing CHEKBOOK.INT into the Business Rules! system:


You may remember that we used #5 as the file number when we first created the CHEKBOOK.INT file; this time it is #7. The file number that you use in an OPEN statement can be any random number from 1 to 127, or it could even be the name of a variable that has a value from 1 to 127. It does not matter which number you choose; the only rule to remember is that all future references to the file must use this same number until the file is closed with a CLOSE statement (or until the program ends).

Notice that the above statement opens CHEKBOOK.INT for input. This is because the information in the file will be input into the program.

Now type in the following DIM, READ and FORM statements:

125 DIM NAME$*25
130 For x=1 to 100
140 FORM X 5,N 10.2,X 13,C 25

The DIM statement in line 125 dimensions the variable NAME$ so that it will hold all 25 characters of the string that will be assigned to it (otherwise, the default limit if 18 characters would apply).

Notice how much the syntax of the READ statement in line 130 resembles the syntax of the WRITE statement you used earlier. However, its function is quite different. READ tells the system to go to the identified file and look up the fields that are identified in the corresponding FORM statement. Once the values in the fields have been located, they are assigned to the variables that are listed at the end of the READ statement for as long as they are used in the program.

The above FORM statement tells the system to skip the first five bytes in the record, but to read the numeric field value in the next ten bytes (refer back to your record layout to find out which field this is). The next 13 bytes are also skipped and then a 25-character string value is read. The READ statement in line 130 tells the system to assign the values to the variables AMOUNT and NAME$.

A program that reads all the records in the file requires some type of looping mechanism, but let’s get ahead of ourselves for a moment and simply add the statements necessary for printing out the requested values from the first record:

160 FORM C 25,X 2,PIC($ZZ,ZZZ,ZZZ.##)

The above two statements will send the information that the system has just read (and assigned to the variables NAME$ and AMOUNT) to the printer.

Notice that the printing order of the values is inverted: the READ statement read them in the opposite order. Accordingly, the FORM statement also specifies format specifications in an order that matches the way the values will be printed. A PIC format identifier is used to format the AMOUNT value with commas in the printed number (if it’s large enough) and no leading zeroes.

There is one other thing about these statements that you should pay attention to. The PRINT statement in line 150 refers to #255 which, as you learned in the printing chapter, is the special number that indicates a printer. This “special number” is actually a file number just as the numbers from 1 to 127 are. Can you remember what the other special file number in Business Rules! is? (Hint: it’s the default file number that tells Business Rules! to send all information to the screen.)

You should now be able to RUN this five-line program without problems. The system will read and print the specified values that are in the first record of the CHEKBOOK.INT file.

The next lesson will show you how to finish this program so that it reads the specified fields from all the records in the file, prints one line for each record in the file, and then prints a total at the end of the report.

Quick Quiz 14.4

1. Which is NOT part of the purpose of the OPEN statement for an existing file?

a) It checks that the file exists.
b) It searches all directories to find the file, wherever it is.
c) It assigns a file number.
d) It specifies the type of use (INPUT, OUTPUT, or OUTIN).
e) It specifies the file type (INTERNAL, EXTERNAL or DISPLAY).
f) It specifies the method of access (SEQUENTIAL, RELATIVE or KEYED).

2. Which one is NOT true about a file number for internal files?

a) It is part of the file identification string.
b) It can range from 1 to 127.
c) It is used as a shortcut way to refer to a file.

Answers 14.4

14.5 Using the READ statement to get information from a file

The sequential method of access (which is what your program is using now) works well when every record in the file must be accessed to obtain the desired information. This method causes the file pointer to always start at the first record in the file and read every single record in the same order that they were entered.

The sequential method of access does not work as well when you need information from only a few records that are scattered throughout the file or when you want information from only a certain section of the records. In these cases and others that you will learn about later, the relative and keyed methods of access work better. You will learn more about these methods of access in the two chapters that follow this one.

Now, let’s finish the program we started in the last lesson. The version of the program on your screen should already include lines 120-160 as they are coded below, with one exception: the EOF error condition has been added to the end of line 130. Make this one change, and then type lines 170 to 230 into your program.

100 ! ******************* L I S T A L C *************************
101 ! PURPOSE: Report program to list and total all checks
102 ! CREATION DATE: 7/22/05 LAST REVISION: 8/25/05
103 ! Business Rules!
104 ! ***********************************************************
125 DIM NAME$*25
130 DO WHILE 1
135 READ #7, USING 140: AMOUNT, NAME$ EOF 200
140 FORM X 5, N 10.2, X 13, C 25
150 PRINT #255, USING 160: NAME$, AMOUNT
160 FORM C 25,X 2,PIC($ZZ,ZZZ,ZZZ.##)
190 LOOP
210 FORM N 5,”Checks for a Total of “,PIC($Z,ZZZ,ZZZ,ZZZ.##)
220 CLOSE #7:
230 STOP

The addition of lines 170 and 180 allows your program to include the current record in a totaling calculation; this calculation will be discussed later in this lesson. Line 190 transfers control back to line 130, where the next record will be read. The cycle of read a record, print, and do calculations is the program loop.

Is there a way out of the loop from lines 130 to 190?

This is where the EOF error condition that you coded into line 130 comes into play. EOF refers to the end-of-file error condition. This error will occur when an attempt is made to read after the last record in the file has already been read. Since this program provides no way out of the loop until the last record in the file is read, the loop will end after the last record has been read.

When the EOF error does occur, the Business Rules! system will trap it because the EOF error condition is coded at the end of line 135. It will send program execution to the line number routine which is located at the line mentioned immediately after the letters EOF (line 200). The program execution continues from line 200.

Now that you see how the loop operates, let’s examine the calculations inside the loop. Line 170 uses an accumulator to add the AMOUNT value of each check to another variable called TOTAL. At the end of the program, the value of TOTAL is the grand total of the amount of all checks.

Each time that line 180 is executed, the number 1 is added to the counter variable called NCHECKS. At the end of the program, the value of NCHECKS is the total number of checks in the file. At any time in the middle of the loop, the number contained in NCHECKS is a count of the number of checks processed so far.

After the last record has been read, printed, and the calculations performed, line 200 (which doesn’t get executed until the loop causes an error by reaching the end of the file) prints the final results of the calculations just discussed. The PRINT statement in line 200 uses the FORM statement in line 210. Notice that the text inside quotation marks in line 210 helps describe the number printed at the bottom of the report. For example, the bottom line might turn out to be:

27 Checks for a Total of $234,567,890.12

Line 220 is a CLOSE statement. Just as the OPEN statement indicates the program is about to start using a file, the CLOSE statement indicates that the program is done using the file. Since the Business Rules! system automatically closes all open files when a program ends, you are not required to close them yourself. The use of the CLOSE statement becomes important only when the same program must open a large number of files; some operating systems limit the number of files you may use at one time, and even if yours does not, you will find that opening and working with too many files at once can severely affect your computer’s performance.

The syntax of the CLOSE statement allows you to close the file and, if you wish, DROP or FREE its contents at the same time. The general syntax of the CLOSE statement is as follows.

(Either the DROP or FREE keywords can be used in place of the optional “file-action” portion):

line# CLOSE #filenum: file-action

The STOP statement in line 230 performs its usual function of ending the program. In addition, if there were no CLOSE statement, the STOP statement would cause all open files to be closed. The STOP, END, and CHAIN statements can all end a program and close all active files.

Once you have this entire program typed in, go ahead and RUN it.

Quick Quiz 14.5

True or False:

1. The READ statement refers to a file by the file number.

2. The colon in the READ statement separates the part of the statement that tells “how” the information should be read from the part that tells “what” information should be read.

3. In the USING clause of a READ statement, a line number or line label must be the location of a FORM statement.

4. The list of items that can follow the colon in a READ statement are called var-names.

5. Var-names can include both variables and constants.

6. EOF at the end of a READ statement is a special var-name that does not have to be preceded by a comma.

Answers 14.5

14.6 Using the REREAD statement with the READ statement

Let’s take a second to visit with our old friend Zippy the Cursor as he reads a record in an internal file. When Zippy encounters a READ statement, he quickly runs to the specified internal file and finds the File pointer, who is pointing directly at the record he should access. Zippy gets the entire record, and then dashes back to the program and refers to the READ’s corresponding FORM statement. The FORM statement tells him which fields within the record are needed; Zippy finds these fields and assigns their values to the variable names that are listed at the end of the READ.

He then goes on to execute the next program line, but he won’t forget the contents of the record that he just read until he executes another I/O statement for that file.

In technical terms, the record that Zippy just read is held in an internal memory buffer (buffer is a fancy term for a temporary storage unit). It stays there until the next I/O statement is executed for that file.

The reason for all this explanation is that we are going to tell you about the REREAD statement; its purpose is to read the record that was just read, again. REREAD can do this very efficiently because the record is still in the system’s memory buffer.

Why would someone ever want to use the REREAD statement? Why not just use READ statements to read all the variables you want? The answer is that the REREAD statement can speed up your programs. This speed increase applies especially in programs that:

  1. must treat different kinds of records differently, and
  2. have lots of numeric fields (because it takes Business Rules! longer to translate numbers from a file to a program than it does to assign strings).

As an example, let’s look at a program which can list and total all deposits (or all withdrawals) from the CHEKBOOK.INT file. This program asks whether the operator wants to list deposits or withdrawals. Suppose the operator asks for deposits (remember that the main way to tell the difference between deposits and withdrawals in the CHEKBOOK.INT file is that deposits have a “D” in position 16, but withdrawals have a “W”).

The program has a loop that reads, prints, and totals. Before reading all the desired fields from every record, it first reads the one field needed to decide whether this record should be included or not. If position 16 does not contain a “D”, then the program goes back and reads the next record. The other fields are read by the REREAD statement, but only if the record has been selected based on information from the READ statement. Execution time is saved whenever the program can avoid reading fields form records that do not need to be included in the report.

Before studying this program in detail, let’s take a look at the syntax of the REREAD statement:

line# REREAD #filenum USING line-ref: varname-list

As is true for all other I/O statements, the use of REREAD requires that the file must first be opened. An additional rule for REREAD is that it can only be executed when it follows a successful READ or another REREAD statement (yes, there are times when you’ll want to use two or more REREAD statements in a row).

00100 ! ******************* L I S T D O R W *********************
00200 ! PURPOSE: List all Deposits OR Withdrawals in CHEKBOOK.INT
00300 ! CREATION DATE: 03/22/06 LAST REVISION : 04/12/06
00400 ! Business Rules!
00500 ! *********************************************************
00700 LP=0 ! Change to 255 for output to printer
00800 PRINT FIELDS “10,21,C 32”: “Enter D to list all DEPOSITS, or”
00900 PRINT FIELDS “12,21,C 32”: “W to list all WITHDRAWALS”
01000 PRINT FIELDS “14,21,C 32”: “E to end program”
01100 PRINT FIELDS “17,21,C 32,b”: “Enter Choice”
01200 INPUT FIELDS “17,37,C 1,rae”: OPT$
01300 LET OPT$=UPRC$(OPT$) ! force OPT$ to be uppercase
01400 IF OPT$=”E” THEN PRINT “Program Complete” : PRINT : STOP !: ELSE IF   
OPT$=”D” THEN LET TYPE$=”Deposits” !:
ELSE IF OPT$=”W” THEN LET TYPE$=”Withdrawals” !:
ELSE PRINT FIELDS “17,37,C 1”: “ “ : GOTO 1200
01600 !
01700 ! file processing starts here
01900 !
02400 LET N=N+1
02700 !
02800 ! end-of-file processing
02900 DONE: CLOSE #5:
03100 STOP
03200 !
03300 ! form statements used above
03400 FORM5: FORM POS 16,C 1
03500 FORM5B: FORM X 5,N 10.2,X 1,N 6
03600 FORMHEADING: FORM POS 20,”List of entire checkbook “,C 20,SKIP 2,POS 36,C 8,SKIP 3
03700 FORMITEM: FORM POS 30,PIC(Z#/##/##),POS 50, PIC($Z,ZZZ,ZZZ.##)
03800 FORMTOTAL: FORM POS 49,”--------------“,SKIP 1,POS 12,N 5,X 2,C 13,”for a total of”,X 4,PIC($Z,ZZZ,ZZZ.##)

Lines 700 to 1200 ask whether the report should include deposits or withdrawals. There is also a third option, to end the program without doing either one. If anything other than one of these three is selected, the program ignores the input and goes back for another choice (see line 1400). The 3-choice screen looks like this:

To make the program more “user-friendly”, Business Rules! allows the report choice to be made with just one keystroke. Single-key input is accomplished with the control attributes “ae” in the INPUT FIELDS statement in line 1200.

A second aspect of “user-friendly” input is that both uppercase and lowercase letters are allowed, using the UPRC$ function to convert any lowercase letters to uppercase before the three tests in line 1400. (The opposite of the UPRC$ function is LWRC$.)

Line 1400 uses a single IF-THEN-ELSE statement to process four possible choices. The four possible cases for operator response are: “E”, “D”, “W” and everything else. Schematically, the structure of this statement could be described as IF-THEN-ELSEIF-ELSEIF-ELSE.

Line 1400 tests first for the “E” or end option. If “E” is selected, the program prints the message “Program Complete” on the screen, then a blank line, and then stops. If either of the tests for “D” or “W” are true, the program sets the variable TYPE$ to “Deposits” or “Withdrawals”, respectively. If none of the first three tests are true, the final ELSE clause erases the operator’s response and branches to line 1200 to wait for another response. Only for a response of “D” or “W” will the program continue with line 1500 where a heading is printed for the report.

If either of the tests for “D” or “W” are true, the program continues processing. Line 1800 opens the file, and lines 2000 to 2600 form the major loop of the program. Line 2000 reads the “D” or “W” in the third field of each record and assigns it to a variable called DW$. The FORM statement with the line label FORM5 (located in line 3400) tells the READ statement that the value for DW$ must come from the single character in position 16.

Line 2100 compares the value of DW$ to the value of OPT$, which represents the operator’s choice about which type of records should be included in the report. If the two values are not equal, the record will be skipped. The GOTO MAINLOOP at the end of line 2100 ends processing for mismatching records because it sends the program back to the READ statement to begin processing the next record.

How does the program get out of this loop from 2000 to 2600? The EOF condition on the end of line 2000 will eventually send control to the line label DONE at line 2900. Should the REREAD statement have an EOF clause too? No. It’s impossible for REREAD to encounter an end-of-file condition because it must always follow a successful READ or REREAD--and the READ statement will not be successful unless there is a record to read.

The REREAD statement is especially useful in cases like the LISTDORW program where only certain records are being selected. Another situation where REREAD statements come in handy is when different types of records should be read in different ways. As an example, consider a program that is designed to print date and check number for all withdrawals. In both cases, the system could be designed to determine the type of transaction (“D” or “W”) with the READ statement; program execution could then be branched to the appropriate REREAD statement, each of which would require its own FORM statement.

Quick Quiz 14.6

True or False:
1. The REREAD statement allows you to read a record that has just been read.

2. You can speed up your programs by using the REREAD statement when selecting records instead of a READ statement for all fields in the record.

3. The EOF error sometimes occurs on REREAD statements.

4. Several REREAD statements could be executed consecutively without an error.

5. After the Business Rules! system reads a record, the record stays in a memory buffer until when?

a) Until the next READ statement is executed.
b) Until the record is deleted.
c) Until the next I/O statement is executed.

6. Which Business Rules! function changes all string input to uppercase letters?

a) UPRC$

Answers 14.6

14.7 Deleting records

The DELETE statement is used to remove a record from an internal file. The file must already be opened for OUTIN. The syntax is as follows:

DELETE #file-num, REC=numeric expression, RESERVE: error-condition line ref


DELETE #file-num, KEY=string expression, RELEASE: error-condition line ref

The file number of the file from which the record is to be deleted must be identified with the "#file-num" parameter.

The optional "REC=num-expr" clause is used with internal files opened for relative or keyed processing, or with external files opened for relative processing. After the numeric expression (usually a simple numeric variable or constant) is evaluated to an integer, the system performs an implied read and the record with that record number is deleted. The REC= parameter may be used with files opened for either relative or keyed processing; using REC= in this manner will not disturb the key position in the file.

The optional "KEY=string-expr" clause is used with files opened for keyed processing; after the string expression is evaluated (usually a simple string variable or constant), the system performs an implied read and that record is deleted.

If neither “REC=num-expr” (for files opened for RELATIVE access) nor “KEY=string-expr” (for KEYED access) is specified, the record deleted will always be the last one read. Note: you cannot specify a KEY for RELATIVE-access files. For SEQUENTIAL processing, the record deleted is always the last record that was read. After DELETE, the next record read will be the next active record after the deleted record. On the other hand, when deleting the record that was just read, you should not use the REC= or KEY= clauses, since they will just generate an unnecessary READ that slows down programs, especially with KEYED processing.

NOTE: DELETE must delete exactly one record when it is used. Therefore, in the KEY=n$ clause, the only form allowed is KEY=n$. KEY>=n$, SEARCH=n$, and SEARCH>=n$ are not allowed, as each of these can return 0 records or more than one record, whereas DELETE must delete exactly one record when it is used. (SEARCH and > are used to list files containing specified parts)

The "RESERVE" and "RELEASE" parameters specify record locking rules for multi-user systems. The default is that records are locked when they are read, and released when they are written. (So that if one person is accessing and editing a file, another person can’t READ and change it at the same time). RESERVE and RELEASE can change this default when desired by the programmer. "RESERVE" instructs the system to hold all previous locks and lock the current record. Locked records cannot be read. "RELEASE" releases all locks and unlocks the current record.

When DELETE is used, the record disappears but its space is not reclaimed automatically. If you want to reclaim that space, the easiest way to do this is using the COPY command with the -D option. (Basically copying the whole file but omitting deleted records). But when doing this, you must rebuild all indexes for that file because the COPY is likely to change relative record numbers, making the index file entries invalid. (Indexing is a way to organize your files).

The positional parameters FIRST, LAST, PRIOR, NEXT, and SAME can be used with the DELETE statement to specify which record to delete from an opened file. These positional parameters can also be used with READ, RESTORE and REWRITE statements. For example:


The DELETE statement may be used to delete any anchor record or subrecord from a linked list. The DELETE statement must be preceded by a successful READ of the record to be deleted.

Business Rules! will automatically update the next and previous record pointers (held in the first eight bytes of each linked record) of the affected records when a linked list record is deleted. If a linked list's anchor record is deleted, the next record in the list will become the anchor record. Also, the value of KREC will be updated to return the value of this new anchor record, no matter where the file pointer is in the list.

Using the REWRITE statement to change information in a file

A slightly more complex task in sequential file processing is updating records, making changes to information that is already in a file.

This job requires a combined effort from the READ statement (which finds the information to be changed) and the REWRITE statement (which performs the actual change).

REWRITE is similar to REREAD in that it must follow a successful READ or REREAD statement before any other I/O statement is used. Unlike the REREAD statement, REWRITE statements cannot follow each other. A record can only be rewritten once for each time that it is read.

To illustrate using the REWRITE statement, let’s look at an example of a program for Checkbook Reconciliation. When your bank statement comes, you need to compare your checkbook records to the bank’s records and resolve any differences--a process called reconciliation. At the beginning of the program the operator enters the date of the bank statement, which will later be output to the file for each record that has been cleared by the bank. This program reads each record (that is, looks at each check); it skips over checks and withdrawals that have already cleared (have a non-zero date cleared); then it rereads records that have not been cleared, and displays them on the screen.

For each record displayed on the screen, the operator types Y or N for either “YES, this record has been cleared by the bank” or “NO, this record has not yet cleared the bank” (and needs to be considered outstanding until the next bank statement comes). When the operator says N, the program does not change this record, but goes on to get the next record. When the operator says Y, the program rewrites the record with a new date in the field for date cleared. Finally, the program goes on to read the next record. The program continues in this loop until the end of the file is reached, and then it displays the total number and total amount of deposits and withdrawals that have been cleared in this session.

Here is the listing for the program, called RECONCYL:

00100 ! ********************** R E C O N C Y L *********************
00200 ! PURPOSE: Reconcile bank statement and write date cleared.
00300 ! CREATION DATE: 7/23/04 LAST REVISION: 8/21/04
00400 ! Business Rules!.
00500 ! ************************************************************
00600 !
00700 ! Get date of bank statement
00900 PRINT FIELDS “10,26,C 30”: “Enter DATE from Bank Statement”
01000 PRINT FIELDS “13,36,C 8”: DATE$(4:8)&”/”&DATE$(1:2) ! CHANGE TO MM/DD/YY
01100 INPUT FIELD “13,36,pic(##/##/##),r”: BANKDATE
01300 !
01400 ! Setup arrays for full screen processing
01500 DIM PFDEFN$(8)*15,DFDEFN$(6)*25,PROMPT$(8)*16,PAYEE$*25
01600 LET PFDEFN$(1)=”6,20,C 13”
01700 LET PFDEFN$(2)=”8,20,C 13”
01800 LET PFDEFN$(3)=”10,20,C 13”
01900 LET PFDEFN$(4)=”12,20,C 13”
02000 LET PFDEFN$(5)=”14,20,C 13”
02100 LET PFDEFN$(6)=”15,20,C 13”
02200 LET PFDEFN$(7)=”17,17,C 16,b”
02300 LET PFDEFN$(8)=”18,30,C 3,b”
02400 LET PROMPT$(1)=”Check Number:”
02500 LET PROMPT$(2)=” Date:”
02600 LET PROMPT$(3)=” Payee:”
02700 LET PROMPT$(4)=” Amount:”
02800 LET PROMPT$(5)=”D=Deposit or”
02900 LET PROMPT$(6)=”Withdrawal”
03000 LET PROMPT$(7)=”Clear this item?”
03100 LET PROMPT$(8)=”Y/N”
03200 LET DFDEFN$(1)=”6,36,N 5,u”
03300 LET DFDEFN$(2)=”8,36,PIC(Z#/##/##),u”
03400 LET DFDEFN$(3)=”10,36,C 25,u”
03500 LET DFDEFN$(4)=”12,36,PIC(Z,ZZZ,ZZZ.##),u”
03600 LET DFDEFN$(5)=”14,36,C 1,u”
03700 LET DFDEFN$(6)=”18,36,C 1,r”
03800 !
04000 !
05300 !
05400 ! End-of-file processing
05500 DONE: CLOSE #5:
05600 PRINT NEWPAGE;”Totals for Bank Statement”
05700 PRINT
06000 STOP
06100 !
06200 ! Function for date conversion MMDDYY to YYMMDD
06300 DEF FNYMD(X)=(X-INT(X/10000)*10000)*100+INT(X/10000)
06400 !
06500 ! Form statements used above
06600 FORM5: FORM POS 23,N 6
06700 FORM5B: FORM N 5,N 10.2,C 1,N 6,X 6,C 25
06800 FORMTOTAL: FORM N 5,X 1,C 11,” For a total of”,PIC($,$$Z,ZZZ.##),SKIP 2

As the remarks indicate, lines 800 to 1400 ask the operator for the date of the bank statement. Lines 1700 to 3900 set up arrays to be used later for full-screen processing.

In line 1000, the system variable DATE$, which contains the current system date, is used as an approximation to the date on the bank statement. Because the system date is a string in YYMMDD format, line 1000 uses substring and concatenation capabilities to change the date to MM/DD/YY format.

Line 1200 calls a user-defined function FNYMD to convert the numeric date entered from YYMMDD format to MMDDYY format on the screen. User-defined functions can be placed anywhere in the program. FNYMD is located in line 6300.

Line 3900 opens the file and assigns a file number of 5. To make changes to existing records in a sequential file, the file must be opened OUTIN. To remember this, it might help to think of updating as first inputting the old information, and then outputting the new information.

The main processing loop starts in line 4100 and ends in line 5200. Lines 4100 to 4300 in this program are similar to lines 2000 to 2200 in the LISTDORW program that you studied earlier. Line 4100 reads the minimal information needed to find out if this record should be processed further. Line 4200 tests the information read by line 4100. In this case, the test is whether the field for date cleared has been set to a value other than zero.

If this date is not zero, this record has already cleared the bank, so the program goes back to the start of the loop to read the next record.

For records that have not already cleared the bank, the REREAD statement in line 4300 gets values for the other fields in this record so that the record can be displayed on the screen by the statements in lines 4400 to 4600. Here is the screen which asks the operator whether or not to clear this item.

Check Number: 12345

Date: 10/22/12

Payee: Corner Grocery Store

Amount: 1,334.44

D=Deposit or

Clear this item?

Again, like the LISTDORW program earlier in this chapter, the operator’s response is forced to be uppercase by line 4800. Line 4900 is an IF statement to consider three different possible operator responses: Y, N or something else. If the operator enters N, this item should not be cleared. If the first test in this IF statement indicates the operator entered N, the program does not rewrite anything, but just goes back to process the next record.

The next case to be considered is when the operator’s reply is neither Y or N. Since the first part of the line has already called out all N responses, the second half of line 4900 needs only to determine that the response was not Y. If it does determine this, then the operator must have entered some other unexpected response. To give the operator another chance at answering the question properly, the input field is erased by writing a blank space on top of the old response, and then the program goes back to the INPUT FIELDS statement in line 4700.

If the operator did enter Y, the program falls through all the parts of line 4900 to line 5000 where it rewrites the record so that the field for date cleared contains the date of the current bank statement. In line 5100, the program totals and counts the value of deposits and withdrawals separately. Line 5300 sends the program back to the start of the main loop to process the next record.

FORM5 (found in line 6600) is the FORM statement used by the REWRITE statement in line 5000. Notice that FORM5 only refers to positions 23-28. It is important to know that the REWRITE statement does not change the other positions in the record. Only positions 23-28 are changed.

When the end of the file is reached, the number of deposits and withdrawals are printed along with their total dollar values.

Do you see any weaknesses in the way this program is designed? Think about what the program does when it reaches the end of the file. Try to come up with something yourself before reading the next paragraph.

If the totals for the computer checkbook do not match the totals for the bank, what choices does the operator have? None! The CHEKBOOK.INT file is immediately changed each time the operator types Y, and there is no way in this program to undo what is done. So if the operator accidentally types Y instead of N even once, there is no way to correct the mistake. If the operator types N instead of Y, the program could be run again, but then the totals on the bank statement would not agree with the computer totals (but hopefully the two sets of computer totals could be added by hand to get the totals from the bank statement).

Programs should not be designed so that the operator is required to never make even one mistake. Programs which do not allow any way to correct mistakes are called “unforgiving”. One characteristic of programs which are “user-friendly” is that they are forgiving.

How could the above program be made forgiving? It would need to allow operators to change their minds about which checks have or have not cleared. This requires that the changes to individual records be temporary; there must be some way to get certain records--perhaps even the entire file--back to the way it was when the program started. There are several ways to do this. You could add another field to the record to indicate which fields are to be changed temporarily. Or you could change the rules for how the field for date cleared is processed, so that it can have temporary values (for example, only write the month and day, then add the year only after the totals have been accepted). The example program did not include any of these provisions, because it would make it harder for you to read and understand the main points; now that you see the big picture, it would be good for you to modify the program to be forgiving.

Another way to make the program forgiving would be to copy the original file to a temporary file, and then make all the changes on the temporary file. If the totals at the end are not correct, then the program would erase the copy and keep the original file. If the totals are correct, the original file would be erased, and the temporary file would be renamed to become the corrected new CHEKBOOK.INT file. This method would be easy to code using Business Rules! commands and Business Rules!’s EXECUTE statement. Add these 7 statements.

05910 PRINT “Are these totals correct?”
05930 YESNO$=LTRM$(YESNO$(1:1))
05970 PRINT “Original file restored. Run program again.” 

Line 4050 makes a backup copy of the file before it is opened in line 4100. Changes are made on the original file. After the totals are displayed in lines 5800 and 5900, the operator is asked if the totals are correct by the new lines 5910 and 5920. (If the operator’s response does not start with the letter Y.)

Quick Quiz 14.7

1. The purpose of the REWRITE statement is:

a) To add new records to a file.
b) To change existing records in a file.
c) To prevent plagiarism.

2. To use the REWRITE statement, a file must be opened with the keyword:


3. When a REWRITE statement updates one field in the record, fields in the record which are not specified in the associated FORM statement:

a) Will be changed to blank spaces.
b) Will remain unchanged.
c) Will automatically be listed on the screen.

4. With files opened for sequential processing, the REWRITE statement can only make changes to:

a) The first record in the file.
b) The last record read.
c) The last record deleted.
d) The next record pointed to by the file pointer.

Answers 14.7

14.8 More about OUTPUT, INPUT and OUTIN

You have now opened the CHEKBOOK.INT file in three different ways: for OUTPUT of information to a file; for INPUT of information from a file; and for both output to and input from the file (with OUTIN). Let’s consider each of these types of file use in more detail.


Files should be opened for OUTPUT whenever you plan to only add new information to the file. OUTPUT implies that no information will be read from the file, and no existing information will be changed. Files opened for output may use the WRITE, DELETE and RESTORE I/O statements. The Business Rules! system does not allow the use of READ, REREAD or REWRITE statements on files which are open for output.


Files should be opened for INPUT whenever the file will only be read, for example, in a program to print a report. INPUT implies that no new information will be added to the file, and none of the current information in the file will be changed. Files which are opened for input may use the READ, REREAD and RESTORE statements, but the Business Rules! system does not allow the use of DELETE, WRITE or REWRITE statements on files open for input.


OUTIN allows both input and output. It must be used when you wish to change information that already exists in the file. Files which are opened for outin may use all six of the I/O statements.

After reading each of these descriptions, you may think that it’s a good idea to always code OUTIN if you are not sure how a file will be used. Especially for multi-user systems, however, this is not a good idea because it can increase system overhead. Also, other programmers expect a program to change a file when the OPEN statement specifies OUTIN. If your programs instead use OUTIN at random, they will be difficult and confusing for others to read. It is better to decide what is needed in the OPEN statement and code exactly what is needed. Also, this prevents accidentally writing or rewriting to a file by mistake (since a file opened for INPUT will throw an error if you attempt to write to it).

Quick Quiz 14.8

1. A program that reads information in a file without changing it should specify which type of use in the OPEN statement?

2. A program that changes information in a file should specify which type of use in the OPEN statement?

Answers 14.8

14.9 The file pointer and the RESTORE statement

Remember Zippy’s trusty friend the File Pointer? This diligent worker always keeps careful track of the next record to be accessed in an open data file. As a programmer, you must be aware of how the File Pointer operates so that you can predict which record will be accessed next.

With sequential file processing, it is fairly simple to figure out which record will be accessed next because the file pointer always moves through the file sequentially, or one record at a time. With relative or keyed access, the file pointer starts where specified in the statement.

Depending on how the file is opened, the file pointer starts out at either the beginning or end of a file that is to be sequentially processed. The file pointer always goes to the first record in the file when the OPEN statement specifies either INPUT,SEQUENTIAL or OUTIN,SEQUENTIAL. If you ever want to send the file pointer back to the beginning of the file to start accessing the same records all over again, you can use the RESTORE statement.

RESTORE is one of the six I/O statements; when used with sequential processing, its syntax requires only the file number of the file to be restored. As an example of using RESTORE, imagine that you have opened CHEKBOOK.INT for sequential processing, but you are only interested in processing the first 20 withdrawal records. (Your program can use a counter variable to keep track of the number of times that the system completes the processing loop; when the value of the variable is 20, then you have processed the first 20 records.) Once this is done, you instruct the program to determine the average amount of each of the 20 checks. You then want it to go back to the beginning of the file and reprocess only those checks that were written for an amount that exceeds the average. The use of the RESTORE statement would cause the file pointer to move back to the beginning of the file even if it’s only half way through (rather than at the end) of the file at the time.

An example of RESTORE when used in this situation could be:

7600 RESTORE #4:

When OUTPUT,SEQUENTIAL is specified in the OPEN statement, the File Pointer knows that you probably want to add records to the end of the file--so he immediately positions himself after the last record. In case he was wrong about his prediction, the RESTORE statement can again be used to reposition the pointer at the beginning of the file. However, RESTORE used on a file opened OUTPUT,SEQUENTIAL will cause all existing records in the file to be erased. After a RESTORE on a file opened for output, the pointer is sitting at the beginning of an empty file. That’s good news if you wanted an empty file, but sometimes it can be bad news. Be careful with the RESTORE statement.

The following two statements would open an imaginary data file called SALESCAL.INT for output of new records, and then empty the file of all existing records (same as a DROP command).

600 RESTORE #5:

Quick Quiz 14.9

Fill in each of the banks below with either “beginning” or “end”:

a) When a file is opened INPUT,SEQUENTIAL, the pointer is positioned at the _____________ of the file.
b) When a file is opened OUTPUT,SEQUENTIAL, the pointer is positioned at the _____________ of the file.
c) When a file is opened OUTIN,SEQUENTIAL, the pointer is positioned at the _____________ of the file.
d) The purpose of the RESTORE statement is to move the pointer to the _____________ of the file.

2. To be able to read sequentially from an internal file, the best way to code the OPEN statement is:


3. True or False: The best way to add records to the end of a long data file for OUTPUT,SEQUENTIAL, then use the RESTORE statement.

Answers 14.9

NEXT:The RELATIVE method of access for INTERNAL files

Back to Index