0% found this document useful (0 votes)
62 views

Coding in RPG IV - A Beginner's Tutorial

Uploaded by

LFN
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
62 views

Coding in RPG IV - A Beginner's Tutorial

Uploaded by

LFN
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

RPG Cafe: Tutorial on RPG

Coding in Free-Form RPG IV


This tutorial is intended for experienced programmers who want to
learn RPG IV (also known as ILE RPG).
This tutorial assumes.

That you have IBM i 7.1 or later so that you can use the free-form version of most RPG IV statement
types. (There may be some coding examples that require later releases. If so, this will be noted with the
example. But 7.1 will be sufficient for most of this tutorial.)

That you have no prior knowledge of RPG III.

That you have some prior knowledge of IBM i (also known as AS/400, iSeries, System i).

That you know where to find the manuals for RPG in the IBM Knowledge Center.

The coding examples are intended to show the feature being discussed, but they sometimes have a "Bonus
features for this example" section following the example that lists additional interesting things to note about
the example, usually things that have not been discussed yet. Readers can choose to ignore those "Bonus
features" sections without worrying about missing something, since the material is covered in later chapters.

Back to top

Chapter 1: Hello world


This chapter takes you through the steps to create and run an RPG IV
program.
It assumes you are using one of two ways to do your coding:

RDi

PDM and SEU through some type of 5250 emulator

Initial setup
If you are using RDi:

Create a connection to your IBM i.

Create a library. Remember the name of the library you create. You need it later when you call your first
program. (Replace yourlibrary in the instructions with the name of the library you created.)

Create a source file.

Create a source member in the file called HELLO.RPGLE.

Open the member in the editor.


If you are using SEU:

Log on to a session on your IBM i.

Create a library by using the CRTLIB command.

Create a source file by using the CRTSRCPF command.

WRKMBRPDM specifying your library and source file.

Create a source member HELLO in the file by using F6 in WRKMBRPDM or by using the ADDPFM
command, giving it type RPGLE.

Open the member in SEU by using the "2" option.

Warning: SEU gives errors for every free-form H, F, D, or P statement. The RPG syntax checker used by
SEU was last updated in 6.1, so it does not understand the new syntax. If you still want to use SEU, you
have to exit with errors after you save your source.

Entering the RPG program statements in "fully-free" form


Type the following code. Be sure that the first **FREE goes in column 1 of line 1. If you are using RDi, the
editor shows you all the columns. If you are using SEU, the editor does not show you columns 1-5 by
default, so the first column you see is column 6. Use F19 to shift the code left so you can see column 1.
**free
dsply 'Hello World';
return;

Compile the program


If you are using RDi, you can compile from the menu within the editor, or you can save your changes
and compile by right-clicking on the member in the navigation. Either way, select the CRTBNDRPG
command, and take all the defaults.

If you are using SEU, exit the editor by using F3, and compile by using option 14. You could also
compile from the command line by using the CRTBNDRPG command, and by using F4 to prompt the
command.

If you are using RDi, log on to a session on the IBM i by using an emulator
Do the following command.
===> WRKOBJPDM yourlibrary

(The WRKOBJPDM command puts you into a list of the objects in that library. You don't actually need
to do this step to call your program, but because of an oddity in how the system determines whether
to halt your job to show you a simple DSPLY message, it's convenient to get into a screen where the
job halts. If you decide not to use WRKOBJPDM, or if it's not available, then your program just ends
without letting you see the DSPLY. In that case, use DSPJOBLOG, then F10, then F6, to see the
output.)

If you are using SEU, exit the editor by using F3, and compile by using option 14. You could also
compile from the command line by using the CRTBNDRPG command, and by using F4 to prompt the
command.
(If you are using PDM, you are already logged on.)

Use the CALL command to call the HELLO program, substituting the name of your library for yourlibrary.
===> CALL yourlibrary/HELLO

It puts up a message saying "DSPLY Hello World"

Just press ENTER to end the program.

"Fully free form" vs "column-limited free form"


RPG has two modes of free-form code. The historical mode (column-limited mode) requires that all free-form
code must be coded between columns 8 and 80. Fully-free mode allows free-form code in any column, with
no limit on line-length.

Fully free-form source must have **FREE in column 1 of line 1. All the code must be free-form.

Non-fully-free source, or column-limited source does not have **FREE in line 1. You can mix fixed-form code
(code that uses columns 6 and 7) and free-form code in column-limited source.

This tutorial assumes you have **FREE at the beginning of your source, but it also works if you use column-
limited source, if you ensure that columns 1-7 are blank and that you don't go past column 80. The examples
have 7 blanks at the beginning to make it easy to copy and paste into column-limited source, but if you have
**FREE at the beginning of your source, you can remove those blanks if you like.

Dealing with compiler errors


Edit your source again, and change it so instead of displaying 'Hello World', it tries to display a variable
called "name". This is an undefined variable and generates a compiler error.
dsply name;
return;

Delete the old version of the program by using the DLTPGM command.

Compile the program again. It fails with a severity 30 error.

If you are using RDi, click the RNF7030 error in the Error List Window. It highlights the line that tried to
display the "name" variable.

If you are using SEU, use the WRKSPLF command and then use F18 to get to the end of the list of
spooled files. Use option 5 on the HELLO spooled file and use the B command to go to the end of the
listing. Page up a bit until you find the list of the error messages, then take note of the error message with
the highest severity (the RNF7030 message). Return to the beginning of the file and enter RNF7030 on
the search line, then hit F16 to locate the error message in the listing. The error message has a statement
number (statement 2). Page back in the listing to find statement 2 (on the left side of the listing). It is the
line that tried to display the "name" variable.

The error message indicates that NAME is not defined. To correct it, you have to add a definition for a
variable called NAME. (Note that RPG IV is not case-sensitive for variable names, so variable "name" is
the same as variable "NAME". The uppercase form of the name appears in the listing in error messages
and the cross-reference.)
Add the following dcl-s statement to the beginning of your program to define variable "name". Remember
to code only between columns 8 - 80.
dcl-s name char(10) inz('Jim');

dsply name;
return;

Recompile the program and call it again. It puts up a message saying "DSPLY Jim".

Debugging
Compile your program again, specifying a debug view other than *NONE or *STMT. For example, specify
DBGVIEW(*LIST). (But *ALL, *SOURCE, or *COPY would all work fine here.)

Now, start the debugger. For now, I'll just mention the system debugger, but there is a more intuitive debugger
associated with RDi that you can investigate separately.

Assuming your program is called QTEMP/MYPGM, do the following commands:

===> STRDBG QTEMP/MYPGM


- at this point, you can just use F10 to exit the screen
- when you call the program, it stops on the first
executable statement
- or you could also use F6 to set a breakpoint on a
particular statement
===> CALL QTEMP/MYPGM
- when you get a breakpoint, step (F10) through the program
- display variables (F11) along the way
===> ENDDBG

That's it!
From now on, I'll assume you know

How to edit and compile your code.

How to locate the error messages associated with a failed compile.

How to call a program.

How to debug a program.

Back to top

Chapter 2: General info about RPG


This chapter introduces you to general information about RPG source
code.

RPG source code


For free-form statements, the RPG compiler uses only columns 8 - 80 of a source member. Columns 1 -5 and
columns 81+ are used for comments. Columns 7 and 8 are only used for fixed form statements. The only
fixed-form statements you need to use are I and O statements, and they are rarely used in RPG nowadays.

There are several types of RPG statements.

Control statements, also called Header statements, also called "H specs" due to the historical fixed-form
statements that start with H in column 6. These contain general-purpose keywords that affect the entire
module.

File and Definition statements, also called "F specs" and "D specs". File statements define the files to be
used in the module. Definition statements define constants, variables, and prototypes.

Input specifications, also called "I specs". These define the input record layouts for your files. The RPG
compiler generates I specs for externally-described input-capable files.

Calculation statements, also called "C specs". Here is where you code the logic of your procedures.

Output specifications, also called "O specs". These define the output record layouts for your files. The RPG
compiler generates O specs for externally-described output-capable files.

Procedure statements, also called "P specs". These start and end subprocedures.

The specifications must appear in the order given above, but it is not necessary to code all the specifications.
A module could contain just a single C spec.

There are fixed-form versions of all the statements, but the only ones you might need to use are I and O
statements, or a few historical types of File or Calculation statements. This tutorial does not cover fixed-form
code. You can see many examples in the ILE RPG Reference.

RPG cycle
RPG was originally created to handle files. The intention was that the program would read a record from the
"primary file" and for each record, it would perform the calculations. When it reached the last record, it would
close the file and end the program.

To artificially create the "last record" situation, the program could set on the "Last Record" indicator, called
*INLR.

For programs that do not have a primary file, it is still necessary to stop the program from looping through the
calculations. This can be done two ways:

By using the RETURN operation.

By setting on the Last Record indicator.


*inlr = '1';

There are subtle differences in these two mechanisms that are discussed in later chapters.

Back to top

Chapter 3: Variables and procedures


This chapter introduces you to defining constants, variables and
prototypes.

Defining constants, variables and prototypes


The Definition statement, often called the "D spec" is used to define constants, variables, prototypes, and
"procedure interfaces". The statement begins with DCL-x where x is C, S, DS, PR, or PI, depending on the
type of item being defined. This is followed by the name, and then one or more keywords, then a semicolon.

Most of the keywords can go in any order you like, but if you have a data-type keyword such as CHAR or
PACKED, it must be the first keyword.

Define a constant
The constant is the simplest RPG definition. It just has one keyword, CONST, and it's optional to actually
specify the keyword. 100 and CONST(100) mean exactly the same thing for a constant definition.

Paste the following code into a source member, then compile and run it.

dcl-c MAX_ELEMS 100;


dcl-c default_city_name 'London';

dsply max_elems;
dsply default_city_name;
return;

Define a standalone field


TThe standalone field is another simple RPG definition. A standalone field is an ordinary scalar variable. It
has a name, a definition type of 'S', and a type.

Paste the following code into a source member, then compile and run it.

dcl-s num int(10);

for num = 1 to 3;
dsply ('i = ' + %char(num));
endfor;
return;

The definition statement defines a field named "num". The INT keyword indicates that the field is an integer.
The "10" indicates that it has 10 digits; this is a 4-byte integer. The "s" in DCL-S indicates that it is a
standalone field.

Bonus features in this example:

The "for loop".

The %char built-in function, which converts the numeric value to a readable character form such as
"-12.345".

The '+' operator acts on strings as well as numeric values.

Exercise 3-1
Define a standalone field of type character with length 5. The data type keyword for character is CHAR.
Code an assignment statement to set the character field to 'hello'.

Display the character field.

Solution

Define a data structure


A data structure is defined with one statement the data structure itself, using DCL-DS. This is followed by one
statement for each subfield. Subfield statements just start with the name of the subfield. The data structure
usually ends with an END-DS statement. If you are defining the data structure like another data structure by
using the LIKEDS keyword, or like a record format by using the LIKEREC keyword, then you don't code END-
DS.

Paste the following code into a source member, then compile and run it.

Note: This example uses the INZ keyword, which provides an initialization for the subfield. You can also code
the INZ keyword for a standalone field.

dcl-ds info qualified;


name char(10) inz('Sam');
salary packed(9 : 2) inz(50000.25);
end-ds;
dcl-ds otherInfo likeds(info) inz(*likeds);

dsply (info.name
+ ' has a salary of'
+ %char(info.salary));
otherInfo.name = 'Joe';
otherInfo.salary += 10000;
dsply (otherInfo.name
+ ' has a salary of'
+ %char(otherInfo.salary));
return;

Bonus features in this example:

The QUALIFIED keyword, which means that the subfields of the data structure must be qualified by the
data structure name, DS.SUBFIELD. Without the QUALIFIED keyword, subfields are referred to just by
their name.

The LIKEDS keyword is used to define another data structure with the same subfields as the parent data
structure. The LIKEDS data structure is automatically qualified.

The INZ(*LIKEDS) keyword is used to initialize the LIKEDS data structure the same as the parent.

The += operator works the same as it does in C and Java™. It adds the value on the right-hand-side to
the variable on the left-hand side.

Define an array
You can define an array of scalars or an array of data structures.

RPG supports only one dimension for arrays. Multiple-dimension arrays can be simulated by using data
structure arrays with array subfields; instead of coding cell(i j k) you would code table(i).row(j).col(k).

The dimension of the array is specified by using the DIM keyword.


Array indexes are specified with parentheses.

Define a scalar array


Paste the following code into a source member, then compile and run it.

dcl-s dates date(*iso) dim(3);

dates(1) = %date(); // the current date


dates(2) = %date() + %days(1); // tomorrow
dates(3) = %date() + %years(1); // next year
dsply (%char(dates(1)) + ' '
+ %char(dates(2)) + ' '
+ %char(dates(3)));
return;

Bonus features in this example:

The date data type with the ISO format (yyyy-mm-dd).

The %date built-in function, which returns the current date when no parameter is specified. %date can
also convert a character or numeric parameter to a "true date".

Define a data structure array


Note: This example requires a 7.2 compiler, and you might need a PTF.
See http://ibm.biz/spring_2017_rpg_enhancements for PTF details.

Paste the following code into a source member, then compile and run it.

dcl-ds families qualified dim(5);


address varchar(50);
numPeople uns(3);
dcl-ds people dim(8);
name varchar(25);
age packed(5);
end-ds;
end-ds;
dcl-s numFamilies uns(5) inz(0);
dcl-s i int(10);
dcl-s j int(10);

families(1).address = '10 Mockingbird Lane';


families(1).people(1).name = 'Alice';
families(1).people(1).age = 3;
families(1).people(2).name = 'Bill';
families(1).people(2).age = 15;
families(1).numPeople = 2;
numFamilies = 1;

for i = 1 to numFamilies;
dsply (families(i).address);
for j = 1 to families(i).numPeople;
dsply (families(i).people(j).name
+ ' is '
+ %char(families(i).people(j).age)
+ ' years old.');
endfor;
endfor;
return;

Bonus features in this example:


The "name" and "address" subfields are defined as type VARCHAR. This means that the string variable
has varying-length; the length given in the definition is the maximum length for the variable. The storage
for a varying-length string variable is prefixed by a 2 or 4 byte value that holds the current length of the
data part of the variable. In the example, the assignment of "Alice" to the name subfield would set the
current length to 5.

The nested "people" subfield of the "families" data structure is defined with the DCL-DS keyword, so it is
both a subfield and a data structure.

The unsigned 5 and unsigned 3 data types. UNS(5) defines a 2-byte unsigned integer that can hold up to
5 digits. UNS(3) defines a 1-byte unsigned integer that can hold up to 3 digits.

Exercise 3-2
Change the data type for NAME to CHAR instead of VARCHAR.

Recompile and run the program.

Explain why the output doesn't look the same as before.

Solution

Define a prototype
RPG prototypes describe how to call a program, procedure, or Java method.

The definition statement starts with DCL-PR. Similar to data stuctures, the DCL-PR statement is followed by
parameter definitions, and then the prototype is ended with an END-PR statement. You always need the
END-PR statement for a prototype.

The EXTPROC or EXTPGM keyword indicates whether it is calling a procedure or program and it also
indicates exactly which procedure or program to call. (Calls to Java methods also use the EXTPROC
keyword.)

If neither the EXTPROC nor EXTPGM keyword is coded, the EXTPROC keyword is assumed.

Example 1: Call a program


A common program to call is QCMDEXC. This program runs a system command.

dcl-pr qcmdexc extpgm('QCMDEXC');


theCmd char(3000) const;
cmdLen packed(15 : 5) const;
dbcs char(3) const options(*nopass);
end-pr;
dcl-s cmd varchar(100);

cmd = 'DSPJOB OUTPUT(*PRINT)';


qcmdexc (cmd : %len(cmd));
qcmdexc ('WRKSPLF' : 7);
return;

Bonus features in this example:

The program name in the EXTPGM keyword is case-sensitive. The system would not be able to find the
program if the RPG program specified 'QcmdExc' in the EXTPGM keyword.
The CONST keyword indicates that the called program does not modify the parameter. When CONST is
specified, the passed parameter does not have to exactly match the type and length on the prototype. If
the type and length don't match, the RPG compiler creates a temporary variable of the required type and
pass that temporary to the called program. Coding CONST also allows literals and expressions to be
passed as parameters.

The call that uses the prototype is coded with the parameters in parentheses. The parameter separator is
a colon, not a comma, as is more usual in other languages.

The %len built-in function returns the current length of the varying-length variable "cmd".

Note: If the RPG prototype name is the same as the actual program name, you can just code EXTPGM with
no parameter. The RPG compiler uppercases the prototype name to determine the actual program name.
Here is an alternate version of the QCMDEXC prototype.

dcl-pr qcmdexc extpgm;


theCmd char(3000) const;
cmdLen packed(15 : 5) const;
dbcs char(3) const options(*nopass);
end-pr;

Example 2: Call a procedure


For this example, we call the C runtime printf() function to print a message to the standard output instead of to
the external message queue.

/if defined(*CRTBNDRPG)
ctl-opt dftactgrp(*no)
actgrp(*new);
/endif
ctl-opt option(*srcstmt);

dcl-s num int(10) inz(25);

print ('This message is much longer than the 52 '


+ 'characters that DSPLY allows. '
+ 'The value of variable "num" is ' + %char(num));
return;

dcl-proc print;
dcl-pi *n;
msg varchar(5000) const;
end-pi;
dcl-pr printf extproc(*dclcase);
template pointer value options(*string);
dummy int(10) value options(*nopass);
end-pr;
dcl-c NEWLINE x'15';

printf(msg + NEWLINE);
end-proc print;

Bonus features in this example:

Instead of calling printf() directly, this example has a subprocedure called print() that handles the call to
printf(). Calling printf() directly is a bit awkward because it has to add the new-line character, so it's
convenient to wrap it in our own procedure.

The print() procedure defines a constant NEWLINE with the hexadecimal value x'15'.
The EXTPROC keyword uses *DCLCASE. This means that the external procedure is "printf", the same
as the RPG prototype, in the same case as the RPG prototype. If we wanted to, we could code
EXTPROC('printf'); we would have to do this if we wanted to use some other name for the RPG
prototype, such as print_to_stdout.

The prototype for printf() has an extra "dummy" parameter. This is required because the C prototype for
printf() indicates that it takes a variable number of parameters. The RPG way to indicate that a procedure
takes a variable number of parameters is to use the OPTIONS(*NOPASS) keyword.
OPTIONS(*NOPASS) indicates that it is not necessary to pass that parameter.

The "template" parameter for printf() is defined as a pointer (the POINTER data type keyword). The
parameter is passed by value (the VALUE keyword). The parameter is defined with the
OPTIONS(*STRING) keyword, which allows you to pass a character string as the parameter. When a
character string is coded as the parameter, the RPG compiler creates a temporary variable with a "null-
terminated" version of the parameter. printf() assumes that the first parameter is null-terminated; the null-
terminator is used by printf() to determine the length of the first parameter.

Conditional compile directives for the first control statement (CTL-OPT statement), /IF and /ENDIF. The
DFTACTGRP and ACTGRP keywords are only allowed with CRTBNDRPG, these directives control
whether those keywords are seen by the compiler. If the CRTBNDRPG command is used,
"*CRTBNDRPG" is defined, and the two H specs between the /IF and the /ENDIF is used in the compile.
If the CRTRPGMOD command is used, those two lines are not included in the compile.

The control statements have three keywords.


DFTACTGRP(*NO): This keyword is required if the program makes bound calls.

ACTGRP(*NEW): This keyword sets the activation group for the program.

OPTION(*SRCSTMT). This keyword causes the compile listing to have the same statement numbers
as the source file. Most RPG programmers use this keyword.

Back to top

Chapter 4: Introduction to files


This chapter introduces you to using 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.
First, remove the USROPN keyword from the F spec.

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?

Solution

Back to top

Chapter 5: Display files


Back to top
This chapter introduces you to using display files, also called
"workstation files".
First, let's create a display file
Paste the following source into a source member with the name RPGDSPF and the source type of DSPF.
The file has one input field, NAME.

A R GETNAME
A 5 5'What is your name?'
A NAME 25A I 5 25
A CHECK(LC)

Compile the file. It creates a display file called RPGDSPF.

Show a screen
Compile and run the following program.

dcl-f rpgdspf workstn;

exfmt getname;
dsply ('Your name is ' + name);
*inlr = '1';

This program just shows the screen and reads the input value. Then it uses the DSPLY opcode to show the
value that it got.

A more complex interaction


Change the display file source to add another record format. The new record format SHOWINFO has some
input/output fields (identified by the B for "Both") and an output field. Recompile the source to get a new
version of the file.

A R GETNAME
A 5 5'What is your name?'
A NAME 25A I 5 25
A CHECK(LC)
A R SHOWINFO CA03(03) CA05(05)
A 5 5'Hello '
A NAME 25A B 5 12
A CHECK(LC)
A CURTIME T O 9 2
A 9 15'Current time'
A DSPATR(HI)
A CONDITION 10A B 10 2
A CHECK(LC)
A 10 15'Current condition'
A 23 2'F3=Exit F5=Refresh'
A COLOR(BLU)

Compile and run the following program. When you get to the part where it shows you the current time, try
changing the condition to something else, say "Raining". The next time you see the screen, it shows
"Raining". Try changing it to something else, say "Warm", but this time press F5 instead of pressing Enter. It
shows "Raining" again. You can also change the name in the same way.

dcl-f rpgdspf workstn;


dcl-s done ind;
exfmt getname;

done = '0';
condition = 'Cloudy';
dow not done;
curtime = %time();
exfmt showinfo;
select;
when *in05;
// Refresh, handled automatically
when *in03;
// Exit
*inlr = '1';
return;
other;
// Just show the screen again
endsl;
enddo;

What's going on in the RPG program?


Compile the program with the listing view DBGVIEW(*LIST) or DBGVIEW(*ALL), and run it under debug.

Set a breakpoint on the EXFMT opcode in the loop that works with the SHOWINFO record format.

Run the program, and see what happens on the EXFMT opcode.

First, it goes to some generated Output specifications, which set the fields into the output buffer from the
RPG program variables.

Then it shows the screen.

Then, after you press ENTER, it goes to the generated Input specifications where it loads the RPG program
variables from the input buffer.

Then it finally goes to the RPG SELECT statement following the EXFMT.

If you pressed F3, the *IN03 indicator was set on. (Due to the CA03(03) coding in the display file source.)

Similarly, if you pressed F5, *IN05 was set on.

If you pressed ENTER, no indicator was set on.

Solutions to exercises
Exercise 3-1
dcl-s myCharfield char(5);

myCharField = 'hello';
dsply myCharField;
return;

Back to Exercise 3-1

Exercise 3-2
The CHAR data type always has the same length, so the variable contains trailing blanks, which are used when
the variable is concatenated with other text.

Bonus question: What RPG built-in function could be used in the concatenation expression to produce the
same output in the version where the variable is defined with the CHAR type rather than the VARCHAR type?

Back to Exercise 3-2

Exercise 4-1
When the RETURN opcode is used, and *INLR is not on when the program returns, the RPG compiler does not
implicitly close the file. The next time the program is called, the file is still open and still at end of file, so
subsequent READ operations do not find any records and the loop exits immediately.

Bonus activity to correct the problem: Add a SETLL *START operation for the file, before the READ loop. That
sets up the file so it is positioned at beginning of file, and the program works the same both times.

setll *start rpgtestf;

Back to Exercise 4-1

Exercise 4-2
When the RETURN opcode is used, the RPG compiler does not implicitly close the file. The next time the
program is called, the OPEN operation fails because the file already open.

Bonus activity to correct the problem: Add a check to see whether the file is already open before using the
OPEN opcode.

if not %open(rpgtestf);
open rpgtestf;
endif;

You might also like