This is a mirror of a wonderful article from RPG Cafe!
(Short URL: https://ibm.biz/rpgCafe_Freeform_Rpg_Tutorial)

Introduction to files

The file statement

The File statement is used to define a file to be used in the RPG module. File statements start with DCL-F (declare file). Like definition statements, the file statement starts with the name to be used for the file in the RPG module. The name is followed by keywords, then a semicolon. If you code a device keyword to say what type of file it is (DISK, PRINTER, WORKSTN), then the device keyword must be the first keyword.

A simple example

Let's start with a little example where we just read all the records of a file.

First, let's get a file to read. Enter the following command on the command line. The command produces a file MYLIB/RPGTESTF that lists the *FILE objects in QGPL whose names start with QRPG. (For this example, change "MYLIB" to the name of your own library, in both the DSPOBJD command and the RPG program)

===> DSPOBJD OBJ(QGPL/QRPG*) OBJTYPE(*FILE) OUTPUT(*OUTFILE) OUTFILE(MYLIB/RPGTESTF)

Now, compile and run the following RPG program.

  • Specify DBGVIEW(*ALL) or DBGVIEW(*LIST) on the compile command so you can get a listing view.

When you run the program, just press ENTER on each DSPLY that shows up.

       dcl-f rpgtestf usropn extdesc('MYLIB/RPGTESTF') extfile(*extdesc);

open rpgtestf;
read rpgtestf;
dow not %eof;
dsply ODOBNM;
read rpgtestf;
enddo;
close rpgtestf;
return;

If MYLIB is not in your library list at compile time and runtime, change your DCL-F command to the following, adding the EXTDESC and EXTFILE keywords so that the system can find the file. Add those keywords to all the examples throughout this chapter that use file RPGTESTF.

       dcl-f rpgtestf usropn extdesc('MYLIB/RPGTESTF') extfile(*extdesc);

If you haven't seen the power of RPG before, you might be wondering where ODOBNM comes from.

Try running it under debug by using the listing view. (If you forgot to compile with DBGVIEW(*ALL) or DBGVIEW(*LIST), compile it again.)

If you compile with DBGVIEW(*ALL), you have to choose the listing view while you are debugging.

  • To use the listing view with RDI, right click in the debug window, click Show View and select "Show *LISTING".
  • To use the listing view with STRDBG, hit F15 and select "ILE RPG Listing View".

When you first see the debug listing view, it looks very different from your original code. You see several RPG statements that were generated by the RPG compiler. These are "Input specifications", and they describe the input buffer of the RPGTESTF file. There is one I spec for each field in the file.

When you step through the program, you notice that you only get a breakpoint on the ODOBNM I spec. That is because the RPG program didn't use any of the other fields, so the RPG compiler did an optimization to avoid loading the data for those other fields.

You also notice that you step to the I spec and DSPLY opcode twice (at least it was twice on my system, once for QRPGLESRC and once for QRPGSRC).

Using externally-described files

The previous program defined RPGTESTF as an externally-described file (to make it a program described file, we would have had to code DISK(100) or whatever the record length is). The RPG compiler "extracts" the file definition as part of the compile, so it knows the names and buffer positions of all the fields in the file.

Most RPG programmers use explicit loops to read a file, but they allow the RPG compiler to implicitly open and close the file. Let's try that.

  1. First, remove the USROPN keyword from the F spec.
  2. Now, remove the OPEN and CLOSE operations.
       dcl-f rpgtestf; 
read rpgtestf;
dow not %eof;
dsply ODOBNM;
read rpgtestf;
enddo;

The RPG compiler implicitly opens the file when you call your program.

But what about closing the file? The RPG compiler does not always close files when a program ends by using the RETURN operation. It only closes files when it finds the "Last Record" indicator, *INLR, to be on. You can simply set *INLR on at some point before reaching the end of calculations, or you can set on *INLR and immediately return. Many RPG programmers set *INLR on as the very first calculation, as a visible clue that the calculations are only meant to be run once. Other RPG programmers set *INLR on at the end of calculations. Either way works fine to cause the calculations to end and to cause the file to be closed.

Try compiling the program. Note that it doesn't have a RETURN operation or code to set *INLR on. The RPG compiler issues message RNF7023 saying that it doesn't know how the program will end. Without a RETURN operation, or *INLR on, the program just loops doing the calculations over and over.

Here is the final corrected program, with *INLR set on as the first statement in the calculations. (Remember that it doesn't matter where *INLR is set on, as long as it is on at the end of the calculations.)

       dcl-f rpgtestf; 
*inlr = '1';
read rpgtestf;
dow not %eof;
dsply ODOBNM;
read rpgtestf;
enddo;

Other types of files

The previous examples all used a database file. The RPG device type for a database file is DISK. This is the default, so we didn't have to code it.

The other two most commonly used device types in RPG are printer files (PRINTER) and display files (WORKSTN). Display files are handled in a later chapter. Here is a little example of a program-described printer file.

       dcl-c QPRINT_LEN 132;
dcl-f qprint printer(QPRINT_LEN);
dcl-ds qprint_ds len(QPRINT_LEN) end-ds;

*inlr = *on;
qprint_ds = 'Hello';
write qprint qprint_ds;
qprint_ds = 'world';
write qprint qprint_ds;

Points to note

  • The PRINTER keyword has a numeric parameter (QPRINT_LEN, which has the value 132). This makes it a program-described file. The RPG compiler does not try to find the file on the system to extract the record layout. Instead, the record layout is coded in the RPG program.
  • In this case, we are just using a flat 132-byte data structure to define the record layout. We specify the data structure as the second operand for the WRITE operation.
  • This program opens and closes the file implicitly. The file gets closed and the program ends after the WRITE statement because *INLR is on.
  • After you run this program, you can find a QPRINT file at the end of your spool files. It has two lines, Hello and world.

Output and update

The previous examples all opened the file for input only. The default usage for a DISK file is USAGE(*INPUT). If you want to write to the file, code USAGE(*OUTPUT). If you want to be able to update the file, code USAGE(*UPDATE). If you want to do both, code USAGE(*OUTPUT:*UPDATE).

Update example

       dcl-f rpgtestf usage(*update);

read rpgtestf;
dow not %eof;
dsply 'new name' '' ODOBNM;
update QLIDOBJD;
read rpgtestf;
enddo;
*inlr = *on;

This program uses the UPDATE operation. Unlike the READ operation, which can be used with either a file name or a record format name, the UPDATE operation can only be used with a record format name. You can use DSPFD to find out the name of the format, or you can just look in one of the RPG compiler listings for the earlier versions of the program.

When you run this program, the DSPLY operation waits until you enter a value. The value you enter becomes the new value for the ODOBNM variable, and that value is used when the record is updated by the UPDATE operation. If you display the file by using DSPPFM, you see that the file name has been changed.

Output example

       dcl-f rpgtestf usage(*output);

ODOBNM = 'ABCDE';
write QLIDOBJD;
*inlr = '1';

The WRITE operation also needs to have the record format name rather than the file name.

When you run this program, it adds a new record to the end of the file. In the new record, all the values are defaulted to blanks or zeros except the file name. Use DSPPFM to display the file again to see the new record.

To see the names of other fields you could set before you do the WRITE, use command DSPFFD RPGTESTF.

Exercises related to implicit open and close

Exercise 4-1

Using the example from 'Implicitly opening and closing the file' as an example, remove the assignment to *INLR, and add a RETURN operation at the end of the calculations.

       dcl-f rpgtestf;

read rpgtestf;
dow not %eof;
dsply ODOBNM;
read rpgtestf;
enddo;
return;

Call the program twice.

Why does the program only produce output the first time it is called?

Solution

Exercise 4-2

Using the version of the program with the USROPN keyword as an example, remove the CLOSE operation (you can just comment it out by using //).

       dcl-f rpgtestf usropn; 
open rpgtestf;
read rpgtestf;
dow not %eof;
dsply ODOBNM;
read rpgtestf;
enddo;
// close rpgtestf;
return;

Call the program twice.

Why does the program get an error the second time it is called?

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
>