AS400 Tip
AS400 Tip
1.Data Areas:
Data Areas are chunks of memory to hold a few control values. A typical use is to keep track of
the last invoice number for a system.
To create a data area, use the command CRTDTAARA (Create Data Area). For example, to
create a 100-character data area named LASTINV#:
A CL program can retrieve the value with RTVDTAARA. An RPG program uses the "IN" operation
to retrieve the value and the "OUT" operation to change it.
There is a special Data Area known as the LDA (Local Data Area). It is 1024 characters and is
associated with a job. So, any display session has an LDA associated with it. Not only that,
when a job is submitted to run in batch, the LDA gets sent with the job so the batch job can
read the LDA of the display session that submitted it.
View and change your LDA by using *LDA instead of a data area name:
DSPDTAARA *LDA
CHGDTAARA DTAARA (*LDA (1 10)) VALUE ('AA12345678')
Using the LDA is considered by many to be an obsolete style. Older programs use the LDA to
store and pass parameters.
When QUERY/400 creates a database, it gives the file and the record of the file the same
name. RPG will not compile if the file name and record name are the same.
So, you must rename the record. For example, if you are reading a file named QRYFIL and that
file was created by QUERY/400, you need to rename the record name to anything unique... in
this case QRYREC
RPG/400
FMT FX .....FFilenameIPEAF........L..I........Device+......KExit++Entry+A....U
RPG/IV
FMT FX FFilename++IPEASF.....L.....A.Device+.Keywords+++++++++++++++++++++++++
1
0116.00 FQRYFIL IF E DISK
0117.00 F RENAME(QRYFIL:QRYREC)
or simply
On the AS/400 every file is in a library. The data in the file is actually stored in individual
members. Typically, a file has only one member and it has the same name as the file.
When you use commands to work with files, there is a parameter that gives you the ability to
choose which member to use. The default for this parameter is *FIRST. So you rarely actually
specify the member name.
So, the data is in a member, which is in a file, which is in a library.
Imagine a sheet of paper with a spreadsheet printed on it. The lines on the spreadsheet are
like the records. If you put only one sheet of paper in a file folder and you put that folder in a
cabinet, if would be similar to the AS/400 file structure:
If you want to see the data in a file, you are actually wanting to see the data in the first and
only member of the file. This is why the command to display the data in a file is not DSPF or
DSPFIL.
The command to display the data in a member of a file is:
DSPPFM - Display Physical File Member
The term physical file refers to the records in the member that are in arrival sequence. With
the database facility, you can build logical views (indexes) to select only certain fields and only
certain records and to resequence the records.
However, there is no Display Logical File Member command. You usually simply view the data
using DSPPFM and use your imagination to picture the logical data. In a while, there will be a
tip for seeing the logical file data.
When you print data on the AS/400 the printer output is written to a printer spool file.
Typically, this is the default printer specified in your user profile.
To see your printer output, use the command Work with Spool File:
WRKSPLF
By using the command this way you are actually specifying:
WRKSPLF SELECT(*CURRENT)
You can look at another user's spool file (if you have authority) by using:
2
WRKSPLF USERXXX
The WRKSPLF command shows a list of all spool files in your queue. It will look like:
Device or Total
Opt File User Queue User Data Sts Pages
FXR001 CHAPGMR P1 RDY 14
FXR001 CHAPGMR P1 RDY 14
If the list is too big for one page, use the PAGE DOWN key to scroll down.
To look at the output file, key a 5 next to the file and hit enter.
When you are done viewing your printer output, hit F3 to return to the spool file list. Key 4
next to the file and hit ENTER to delete it.
To get a quick look at the records in a physical file, use the Display Physical File Member
command (DSPPFM).
To see the unformatted data in the file PR001:
DSPPFM PR001
To get a formatted look at the records, use the Run Query (RUNQRY) command. Essentially,
this command builds a query to view the data:
RUNQRY QRYFILE(PR001)
3
or key in RUNQRY, hit F4 and fill in the file name. This command displays the data in columns
with the field names. It also formats the data, including packed numeric fields that look like
garbage with DSPPGM command.
The first step is to run selection 1 to collect disk information. This starts a job that may run for
15 minutes or so.
When the job is done, use selection 2 to print reports from the disk info. The most interesting
are:
1. Print all libraries sorted from largest to smallest without file details.
2. Print details of any library to see each file and how much space it is using.
When changing a display file using SDA (Screen Design Aid), the added fields are placed at the
bottom of the DDS program when it is saved and compiled. This can make it difficult for the
user to easily locate the changed fields. To re-sort the fields before exiting SDA, hit the F4 key,
then F6, and the statements will be placed in order.
There are times when you may have the same field name in different programs with varying
lengths. To get a clean compile, it may be necessary to use the following I-spec coding.
FMT I IFilename++SqNORiPos1+NCCPos2+NCCPos3+NCC..............................
0029.00IACTMAS
0030.00I CALL24 XALL24
In order to customize the AS/400 sign-on display, you must first create the DDS that you want
to use for your sign-on display. There are 2 ways to accomplish this: modifying IBM's sign-on
display, using member QDSIGNON in the physical file QGPL/QDDSSRC; or by writing your own
DDS and copying the fields in QDSIGNON. In either instance, all IBM fields must remain in the
4
same order in the display file, but you can reposition them on the display. The only IBM field
that can be modified is the UBUFFER field, which is the last field. The 128-byte field can be
modified with input/output fields thus allowing users to input data into the sign-on displays.
The UBUFFER fields can appear anywhere on the display, but they must follow the IBM fields in
the display file.
Compile the display file. Once the display is created, be sure to test the new sign-on display on
one subsystem or on a test version of a controlling subsystem. By issuing the CHGSBSD (Change
Subsystem Description) command, and specifying the new display file on the SGNDSPF
parameter, the subsystem will now use the new sign-on display. Once testing is complete, you
can change the subsystem descriptions for all the subsystems for which you want to use the
modified sign-on display.
For example, for subsystem QINTER, issue the command CHGBSD QINTER SGNDSPF(your
library/your file)
In this program, you will find 2 CL programs and 1 RPG-ILE program that will create a file
named ALLFILES. You can write a query on this file and sort on object size or last date changed
to help identify good candidates for deletion.
--------------------------------------------------------------------------------
5
0005.00 OUTPUT(*OUTFILE) OUTFILE(QTEMP/ALLFILES)
0006.00 MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(EOJ))
0007.00/* */
0008.00 CHKOBJ OBJ(your library/ALLFILES) OBJTYPE(*FILE)
0009.00 MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(FRSTTIME))
0010.00 CPYF FROMFILE(QTEMP/ALLFILES) +
0011.00 TOFILE(your library/ALLFILES) MBROPT(*ADD)
0012.00 GOTO EOJ
0013.00/* */
0014.00FRSTTIME:
0015.00 CPYF FROMFILE(QTEMP/ALLFILES) +
0016.00 TOFILE(your library/ALLFILES) MBROPT(*ADD) +
0017.00 CRTFILE(*YES)
0018.00EOJ:
0019.00ENDPGM
****************** End of data ****************************************
Figure 3 - RPG-ILE Program Run by CL Program to List Library and Information - UTR001
6
0031.00C CheckLib BegSr
0032.00 *
0033.00C MoveL ODOBNM Library
0034.00C If Library <> 'Q' and
0036.00 *
0037.00C Call 'UTC0002'
0038.00C Parm ODOBNM
0039.00C EndIf
0040.00 *
0041.00C EndSr
****************** End of data ****************************************
There are times when you will be writing an RPG program that uses both physical and logical
files. When using both a physical file and logic file, both having the same record name, and
updating or writing to records, the keyword RENAME must be used to differentiate between
which record name is to be updated. Figures 1 and 2 show the file specs for RPG and RPG ILE.
FMT FX .....FFilenameIPEAF........L..I........Device+......KExit++Entry+A....U
0059.00 FTCLTRNK1IF E K DISK
0060.00 FTCPRACY UF E K DISK
0061.00 FTCPRACY1O E DISK A
0062.00 F RACY KRENAMERA1
0063.00 FTCPRACY2O E DISK A
0064.00 F RACY KRENAMERA2
0065.00 FTCLRACY E DISK A U
0066.00 F RACY KRENAMERA3
FMT FX FFilename++IPEASF.....L.....A.Device+.Keywords+++++++++++++++++++++++++
0114.00 FTCLTRNK1 IF E K DISK
0115.00 FTCPRACY UF E K DISK
0116.00 FTCPRACY1 O A E DISK
0117.00 F RENAME(RACY:RA1)
0118.00 FTCPRACY2 O A E DISK
0119.00 F RENAME(RACY:RA2)
0120.00 FTCLPRACY O A E DISK USROPN
0121.00 F RENAME(RACY:RA3)
Create a physical file of 50 to 80 characters to hold the FTP commands. Name it something like
FTPIN.
Then use DFU to enter the FTP commands. The first record should be your user ID and
password. Then the following records have the remaining FTP commands.
7
For example, to sign on as davidmount with password of as400, then change directory to
UPLOAD and then PUT a file, the records should look like:
davidmount as400
cd UPLOAD
put xyzzy.txt abc.csv
quit
You may also create a file for the output. Say... FTPOUT
Then, the CL to execute overides the specially named INPUT file and OUTPUT file.
What would normally be displayed on the screen as output is written to the FTPOUT file. This is
very powerful. You can (with some effort), use FTP to get a directory of the files on a remote
system. Then, you can read thru the directory (it's in the FTPOUT file) and then build other FTP
scripts to GET specific files and even delete specific files.
In a related matter, to check to see if a transfer is complete, you must read through the
FTPOUT file to see if you find the phrase "Transfer complete" in a record somewhere.
OS/400 will NOT return an error if you do not successfully transfer the data. It will simply write
the output display to the OUTPUT file and consider this a successful execution of the FTP.
A cursor is defined in the RPG or RPGLE source code. Then FETCH is used in a loop to access
the data from the file. In this loop to process the fetched data, RPG/RPGLE code can be used
and the output can be sent to the spool or to the display.
C/EXEC SQL
C+ INCLUDE SQLCA
C/END-EXEC
C/EXEC SQL
C+ DECLARE GETREC CURSOR FOR SELECT XABCDE, XFGHIJ, XKLMN, FROM
C+ XFILE WHERE
C+ XSTRIN LIKE :STRING
C/END-EXEC
:STRING IS A VARIABLE
C Fetch begsr
8
C
C/EXEC SQL
C+ FETCH NEXT FROM GETREC INTO :XABCD, :XEFGH
C/END-EXEC
ENDSR
C Close begsr
C
C/EXEC SQL
C+ CLOSE GETREC
C/END-EXEC
C endsr
C Open begsr
C
C/EXEC SQL
C+ OPEN GETREC
C/END-EXEC
C endsr
EXSR OPEN
sqlcod doune 0
exsr Fetch
rpg code
rpg code
enddo
exsr close
The source type for RPG/400 with embedded SQL is SQLRPG, with ILE it is SQLRPGLE.
When you are signed on to an AS400, you can view your job log by keying the command
DSPJOBLOG. Then you must hit F10 (to see details) and use PAGE UP to see the log.
15.Preventing Third Party Relay Spam on Your iSeries AS400 SMTP Server
If you have the SMTP (e-mail) server running on your iSeries / AS400, you may have had the
misfortune of learning about third party relay spammers. These are people who use your e-mail
server to deliver their spam. Not only will this overload your server, it will inevitably get your
domain listed as one that sends spam. With release V4R4, OS/400 provides a way to prevent
third party relay of mail. V4R3 has a PTF for this.
The method to do this involves defining the TCP/IP addresses that may send e-mail on your
server. In my case, it is the specific address of my AS/400. In your case, it may include
everyone on your network.
9
Create a source physical file in library QUSRSYS named QTMSADRLST. The file must be created
with CCSID value set to "500".
Start PDM on this new source file and create a new member named ACCEPTRLY. For each IP
address that should be able to send e-mail, you add a line to this file member. Each line will
contain an IP address and an address mask.
66.12.223.253 255.255.255.255
An entry like this would allow any address starting with 66.12.223 to send e-mail:
66.12.223.1 255.255.255.0
The AS/400 has a built in debugging tool, though very powerful, it is not completely intuitive.
Sometimes you may find yourself in need of a procedure that can help you locate program
errors that you cannot visually find by looking at your program. RPG-ILE's program debugger
can be very useful in these situations. It allows you to trace a program as it is executing,
stepping through a program a statement at a time, or stopping at "breakpoints" that you
designate so you can see the values of fields at that point of execution. Follow the steps below
to start the "debugging" mode for your programs.
First, you must compile the program in an appropriate mode. Do this from PDM by keying a 14
next the program source and hitting F4.
Now, hit F10 to get more options. Key in *LIST as the value for "Debugging View".
The screen should look like:
10
Type choices, press Enter.
Additional Parameters
Now ENTER to compile your program. If this program calls other programs, you may want to
compile them in debug mode also.
Now it gets a little tricky. Start the debugging mode with the "Start Debug" for the program you
want to debug. In this case, the program is LXR001 so the command to use is STRDBG LXR001.
Key in STRDBG LXR001 and hit F14:
If your program updates or writes data to a file in a library that is defined as *PROD, you must
change the "Update production files" option to *YES. Then, hit ENTER.
The debugger now displays the source code of the program. You MUST define a breakpoint
where the debugger will actually start debugging. The easiest thing to do is to page down to
the first executable statement of the program. Put the cursor on that line of source code and
hit F6 to define that line as a breakpoint.
Your session is now in "debug" mode. It will stay in "debug" mode until you end debug mode
with the command ENDDBG.
11
Finally, it is time to start running your program. Call the program from the command line. In
this case: CALL LXR001
The program will start running. The debug feature will display a listing of the program. The
display is showing that the breakpoint is about to execute. You can scroll up or down, position
the cursor on any field and hit F11. The debugger will display the value of the field.
Now hit F10 repeatedly to step through the source code. The line of code that is about to
execute will be highlighted.
Note that the debugger has the annoying habit of stepping through every field in each file that
is read or updated. To prevent this, add a "header" record as the first line of code in the
program. "Header" records are not required and not usually used. Use an "H" as the record type
with a value of "option(*nodebugio). That is, add this line to the beginning of your program:
H OPTION(*NODEBUGIO)
When you are finished debuggin, key ENDDBG to turn off the debugger.
To summarize:
3) Start the debugger with the command: STRDBG pgmname (Update Production files *YES)
4) Put cursor on the first line that you want to see and hit F6. Exit with F3.
6) Use F10 to step through the program. Use F11 to see values.
7) If you wish, set additional breakpoints and use F12 to run until the next breakpoint.
Submit your program into batch and make sure it is held. Do this by either holding the job
queue that you are submitting the job to, or use the HOLD(*YES) option on the SBMJOB
command.
Use the WRKUSRJOB and display the job you with to debug with the display job option (5).
Start a service job using STRSRVJOB entering the name, job name and number from the
previous step.
STRDBG PGM(YOURPGM) - Press F12 to exit the source display (Sorry, can't enter breakpoints
yet). Release your submitted job by releasing the job queue or the job itself.
A display will appear asking you to press F10 function key. Press F10 and you will be brought to
a command line.
12
Enter DSPMODSRC and enter your breakpoints. Leave source display and command line by
pressing F3 until you are back to the screen that asks you to press F10 to enter breakpoints.
After that the job begins running and stops at the first breakpoint reached.
To view the results of a program compiling, use the Work With Spool File command, WRKSPLF.
You will see a list of reports in your spool file. It will look like:
Device or Total
Opt File User Queue User Data Sts Pages
QSYSPRT AIRPGMR PRTDGM RDY 1
QPJOBLOG AIRPGMR QPRINT AIRNRA RDY 2
QSYSPRT AIRPGMR PRTDGM RDY 1
QSYSPRT AIRPGMR PRTDGM RDY 1
QPJOBLOG AIRPGMR QPRINT TQE07 RDY 2
QSYSPRT AIRPGMR PRTDGM RDY 1
QSYSPRT AIRPGMR PRTDGM RDY 1
Each line is the name of a report on your Spool File. The most recent reports are at the
bottom. Key a 5 next the report you want to view.
You should now be looking at the compiler listing. To quickly locate errors, key B (for bottom)
in the CONTROL field and hit ENTER. You are now looking at the bottom of the compiler report.
If your program compiled successfully, the last few lines will look like:
To find the errors, hit the PAGE UP key until you see:
13
These are the errors that need correcting. Anything with severity level 00 is NOT an error.
To find the error, go to the top of the report. Do this by keying T in the CONTROL field and
hitting ENTER.
Then, key the error message ID in the FIND field. In this case, I keyed RNF7030 in the FIND
field. Then, hit F16 to search. Remember F16 is SHIFT / F4.
The errors RMF7030 and RNF7503 need to be fixed. They are both related to sequence # 123 in
the compile listing which is line number 52.00 in the source file.
In my case, I misspelled a variable name. This caused 2 errors. Often, 1 error will cause more
than 1 error message.
One of the most powerful commands on the AS/400 is the Copy File command. To use it, key
CPYF and hit F4.
To copy the file ABC from MYLIB to ABCBKUP in MYLIB, fill in the screen like this:
14
From member . . . . . . . . . . *FIRST
To member or label . . . . . . . *FIRST
Replace or add records . . . . . *REPLACE
Create file . . . . . . . . . . *NO
Print format . . . . . . . . . . *CHAR
Hit F10 to see the 5 extra screens of options. Use PAGE DOWN to get to the different screens.
To copy only 100 records, key 100 in the "Copy to Record Number" parameter. Or, use the
"Number of records to copy" parameter.
To add data from the file ABC01 to ABC, use *ADD for "Replace or add records":
To see all jobs running on your system, use the Work with Active Jobs command, WRKACTJOB.
Each sub-system will be displayed with the jobs running in it. Every terminal signed on the
system is a job. There are many system jobs running to do things like spool reports and service
Telnet and FTP.
The first sub-system listed is often QBATCH. This sub-system runs user submitted jobs.
The sub-system for on-line users is usually named QINTER. Most of the users signed on to your
system will have their job running here.
Put the cursor in a column and hit F16 (Shift-F4) to sort the display. To see the users that are
consuming most resources, put the cursor in the CPU% column and hit F16. To sort the users
alphabetically, move the cursor to the User column and hit F16.
To get help for menu selections, key the number of the selection and then hit F1 instead of
ENTER.
To get help for a command, key the command, put the cursor on the command and hit F1.
15
To get prompting information for a command, key the command and then hit F4. If
"F10=ADDITIONAL PARAMETERS" is at the bottom of the screen, hit F10 to get prompts for all
keywords. You may need to PAGE DOWN to additional screens.
The AS/400 has an excellent job scheduler. It will submit any job at a pre-scheduled time.
From the Job Schedule menu, hit F6 to add a job. Simply fill in the values to define when and
how often the job runs.
Use anything for the job name. The "Command to run" is what will be submitted.
To run a program named ABC001 from the PROD library every night at 3:00am:
The only surprise with the job scheduler is that it uses the default library list. So, in this case,
the job ABC001 might need to establish a correct library list as its first few commands.
In your library list, the first user library is the current library. The predecessor to the AS/400,
the System 38, did not have a current library concept. To allow for this, a special value
*CRTDFT is allowed.
The default value of the library parameter of some commands is *CURLIB. For example, if you
use the Create Physical File command, CRTPF and do not specify a destination library, the
system will create the file in your current library. But, if your current library is defined as
*CRTDFT, the system will create the file in QGPL. This library, the "General Purpose Library", is
similar to the root directory in a PC. That is, if the system does not know where to put
something, it will likely end up here.
System Administrators fight to keep garbage out of QGPL. For many administrators, the G in
QGPL stands for Garbage.
Another system supplied library is QTEMP. Every job has its own QTEMP libary. The library gets
cleared everytime the job ends. So, this is a great place to put truly temporary files.
16
24.Running Jobs in Batch or Background
Most AS/400's are configured so on-line sessions run in the subsystem named QINTER. Usually
these jobs are assigned a job priority of 30. This means that these jobs get serviced before any
jobs with a higher priority number.
For jobs that can run unattended, use the command to Submit Job. For example, if you have a
program that prints a report and it is named PRT100, submit the job to the batch subsystem
with this command:
If you wish, use F4 to see the command options. Typically, this will submit the job to run in the
QBATCH subsystem with a job priority of 50. This means this job will be serviced only after all
the interactive or on-line jobs in the QINTER subsystem.
When a job is submitted, it brings along the library list and the local data area of the job that
submitted it.
Generally, a file has to be defined before you can write records to it.
Sometimes, you may need a simple "flat" file with no field definitions. For that, the Create
Physical File command without DDS specifications will work. To create a file named FTPOUT
that is 100 characters in length:
The AS/400 actually creates a file named FTPOUT, a member named FTPOUT and a field of
length 100 that is named FTPOUT.
To make a database file, you use the DDS specifications. You must start the Programming
Development Manager and create a member with TYPE of PF (for physical file). Usually, this
goes in the object QDDSSRC.
The first line defines the record name. For a simple 3 field file:
A R CUSREC
A NAME 20A
A PHONE 10S 0
A STATUS 1A
Name the member in QDDSSRC the same as the desired file. In this case, I named it CUS.
Then, you must compile or create the file. In PDM you can do this with the option 14. When you
use option 14, you actually execute the command:
The AS/400 knows to use this command because you specified that this member is describing a
PF.
17
If you have wanted a key built on the PHONE field, the DDS would have been:
A R CUSREC
A NAME 20A
A PHONE 10S 0
A STATUS 1A
A K PHONE
Physical files hold the actual data of a database file. The data is written in arrival sequence.
Physical files are not required to have keyed fields. If a physical file has key fields, this is the
order that an RPG program will read the data if the File Spec in the program indicates to read
the data in keyed sequence.
Also, with a keyed field, an RPG program can CHAIN, SETLL, READE and READP.
A simple logical file is a different view of the physical file. It is actually a list of pointers to the
physical file. Most of the time, a logical file is nothing more than a way of accessing the
physical file with different key fields.
With the standard AS/400 supplied tools, it is hard to see the logical file. One way is to use the
copy file CPYF to copy the logical file to a new physical file. Then, look at the physical file... it
will be in the same order as the logical file.
The AS/400 Database is full featured. Logical files can join multiple files and select and create
new fields.
The most common and simplest use of logical files is to change the order of the data.
To define a logical file, you use the DDS specifications. You must start the Programming
Development Manager and create a member with TYPE of LF (for logical file). Usually, this goes
in the object QDDSSRC.
Here is how to make a logical file named CUS01 which orders the CUS physical file by name.
For simple logical files like this, the first line defines the record name of the physical file that
has the data. This line also has the PFILE function that names the physical file.
A R CUSREC PFILE(CUS01)
A*
A K NAME
Name the member in QDDSRC the same as the desired file. In this case, I named it CUS01.
18
Then, you must compile or create the file. In PDM you can do this with the option 14. When you
use option 14, you actually execute the command:
The AS/400 knows to use this command because you specified that this member is describing a
LF.
Add a SELECT spec to select only customers with STATUS of 'A'
A R CUSREC PFILE(CUS01)
A*
A K NAME
A S STATUS COMP(EQ 'A')
An RPG program uses logical files almost identically to physical files. In this last example, an
RPG program that reads the file CUS01 would automatically bypass any records with STATUS of
D.
OS/400 has an FTP Client that works a lot like FTP from a MS-DOS command line.
From the command line enter either the command FTP or, the OS/400ized name, STRTCPFTP
and hit F4.
To schedule this command to run at 2:00 am on October 27, use the job scheduler. Key in
WRKJOBSCDE
19
Schedule date, or . . . . . . . 10/27/2002 Date, *CURRENT, *MONT
Schedule day . . . . . . . . . . *NONE *NONE, *ALL, *MON, *T
+ for more values
Schedule time . . . . . . . . . 02:00:00 Time, *CURRENT
I named my job FALLBACK and scheduled it to run once at 2:00 am on the day that we set the
clocks back.
You may find yourself in a situation where you want to run a Query on a file, save the
information to a database,and then convert that information to a Comma Separated Variable
(CSV) file. This is especially true when you need the data to be used in a "spread sheet" format
like Excel.
The simplest way to do this is to copy the information from the data file to your newly created
CSV file.
Hit Enter, and then again 3 times and you have now created a Comma Separated Variable (CSV)
File.
Remember that you need a 'destination file' before you can use this command. To create a
'destination file', you will need to use the Create Physical File command. To create a file
named CSVPC that is 200 characters in length, type in the command:
CRTPF FILE(USER999/CSVPC)
Hit F4 to prompt the command, and type in the record length you need, for Member, be sure to
name it CSV.
More...
20
F3=Exit F4=Prompt F5=Refresh F10=Additional parameters F12=Cancel
F13=How to use this display F24=More keys
Then hit F10 for Additional Parameters, and type in *NOMAX in the "Initial number of records"
field.
Additional Parameters
Now you have created a 'destination file' and can convert an AS/400 database to a CSV file.
To view the file you have created, simply type in this command:
DSPPFM USER999/CSVPC
21
"L","35","ORIGINATING NUMBER INVALID ",
"R","36","ORIGINATING STATE INVALID ",
More...
F3=Exit F12=Cancel F19=Left F20=Right F24=More keys
To see what user signed on to a terminal, use the Display Workstation User command,
DSPWSUSR.
After you set up client access and make sure that you install the ODBC driver, click on START,
SETTINGS, CONTROL PANEL. If you don’t see ”Data Sources (ODBC)” on the first screen, but see
“ADMINISTRATIVE TOOL”, click on ADMINISTRATIVE TOOLS, then DATA SOURCES(ODBC).
Click Add
22
Click Finish
23
Click Apply
24
Then click OK..Now you should be able to connect to the database. On the above screen, if you
want READ ONLY rights, make sure you click the read only option.
FTP is an acronym for "File Transfer Protocol". FTP is a powerful application which allows users
to access archives that are available on a large number of computer hosts. FTP is a
client/server application that allows the transfer of files between computers. This transfer can
take place between a mainframe and a local terminal, or as a transfer of information over the
Internet between your computer and a distant server. The key elements of FTP are:
25
put to copy one file from the local machine to the remote machine
pwd to find out the pathname of the current directory on the remote machine
quit to exit the FTP environment (same as bye)
rmdir to to remove (delete) a directory in the current remote directory
34. Updating Data Areas
It seems like there are three different ways to solve any problem on the AS/400. Even
experienced IS professionals sometimes loose sight of alternate approaches to application
design. In this training track series I present basic AS/400 skills to help you keep those solutions
in sight. I'll show you how to use data areas to store control values for a fictitious invoicing
system.
Your invoicing system needs to keep track of the last invoice number used. You might also want
to store the sales tax percentage in a way that makes it easy to change. Finally, suppose you
have a standard handling charge that is added to each invoice.
One possible approach to storing these control values is to create a database file with one
record. That record would have a field for last invoice number, another field for sales tax
percentage and another for handling charge. The invoicing program would chain to this record
and use those values. You could then write a maintenance program so the administrator of the
system could change the values.
The AS/400 offers another approach. You could create a data area to store these values. Think
of a data area as a chunk of memory that is similar to a file with one record. You can change
the value of any character in the data area and you can retrieve the values from any program:
CL, RPG and COBOL.
To see the different ways of using data areas, I'll show you how to:
2) retrieve, use and update the data area using an RPG program
3) use a CL program to give the system administrator an easy way to change the values
When you create the data area, you will need to supply a name and length. The name should
follow your naming conventions. For this example, name your data area "INVA100". You will
also need to supply a length for the data area. It doesn't hurt to create a little extra room, so
make this data area 100 characters in length. Finally, you will specify the library where the
data area will reside. So, to create a data area named "INVA100" in library "INVFILES", just key
in the command:
You now have 100 characters of storage to work with. Suppose your invoice numbers are 7
positions long and your last invoice number was 100324. You want to change the value of the
first 7 characters of the data area to 0100324. You do this with the "Change Data Area"
command:
26
Then use these commands to store your sales tax of 7.125% in positions 8 through 12 and your
handling charge of $2.00 in position 13 through 17:
Now use this "Display Data Area" command to display your data area:
DSPDTAARA DTAARA(INVFILES/INVA100)
The AS/400 will show that you have successfully stored your control values in the data area.
You have probably noticed the free format nature of this data area. There are no database
fields or special attributes for the data area. It will be up to you to keep track of the location
of each value in the data area.
I won't look at a complete invoicing program here. The important thing for now is that your
RPG program will need to retrieve the data area at the beginning of the program. Also, when
the program has finished creating the invoices, it needs to update the value of the last invoice
number.
Figure 1 shows the RPG code to retrieve and update the data area. It retrieves the data area
with the "IN" instruction. This instruction reads the 100 characters in the data area named
"INVA100" into the data structure named "ControlValues". This data structure breaks up the
data area into the three fields that you need: CtlLastInv#, CtlSalesTax and CtlHandling.
When the main logic of the program creates the invoices, it will increment the invoice number.
Before terminating, the program uses the "OUT" instruction to update the data area. After the
program runs, you could use the "Display Data Area" command to see that the value of the last
invoice number in the data area has changed.
You could document how the system administrator could use the "Change Data Area" command
to modify the sales tax and handling charge. It is more user friendly to write a program to
format the fields on a display file. You could write an RPG program that retrieves the data area
with an "IN" instruction, displays it using a display file and updates it with an "OUT" instruction.
Since I want you to see different ways of using data areas, I'll use a CL program to display and
change the values in the data area.
CL programs can declare one file. If this file is a display file, the CL can display and read a
screen. If you code and compile a simple display file, a CL program can use it to provide an
easy way to work with the values in the data area.
I can't leave the topic of data areas without mentioning the "Local Data Area". Every job
running on the AS/400 has a system defined data area of 1,024 characters. This means that any
interactive job has this area available at all times. Though this isn't considered a modern
27
programming style, many programmers use the "Local Data Area" as a way to pass parameters
between programs. You can use the "Display Data Area" and "Change Data Area" commands to
work with the "Local Data Area". Instead of specifying a data area name, use "*LDA" to indicate
that you are referring to the system defined "Local Data Area". Realize that each job has its
own "Local Data Area" which is cleared when you sign off.
--------------------------------------------------------------------------------
Figure 1 - This is all you need to read and update the data area from an RPG program.
0001.00 * This defines the data structure that defines the fields
0002.00 * of the data area named INVA100
0003.00 D ControlValues DS DtaAra(INVA100)
0004.00 D CtlLastInv# 1 7 0
0005.00 D CtlSalesTax 8 12 3
0006.00 D CtlHandling 13 17 2
0007.00 D CtlFiller 18 100
0008.00 * First time routine to retrieve the data area
0009.00 C *Lock IN ControlValues
0010.00 *
0011.00 * The main logic of the program goes here
0012.00 *
0013.00 * Before ending program, update the data area since CtlLastInv#
0014.00 * has changed
0015.00 C OUT ControlValues
0016.00 C Eval *InLR = *On
0017.00 C Return
Figure 2 - This is the CL program to easily modify the values of the data area.
0001.00 PGM
0002.00 /* This DCLF (declare file) names the display file that will be used */
0003.00 DCLF FILE(INVD100)
0004.00 /* These Retrieve Data Area commands read the data area and put the */
0005.00 /* the values in the fields in the Display File */
0006.00 RTVDTAARA DTAARA(INVA100 (1 7)) RTNVAR(&DLSTIN)
0007.00 RTVDTAARA DTAARA(INVA100 (8 5)) RTNVAR(&DSLSTX)
0008.00 RTVDTAARA DTAARA(INVA100 (13 5)) RTNVAR(&DHNDCH)
0009.00 /* Send Receive File shows the display & then reads what is entered */
0010.00 SNDRCVF RCDFMT(SCR1)
0011.00 /* These Change Data Area commands store what was entered back in the*/
0012.00 /* Data Area */
0013.00 CHGDTAARA DTAARA(INVA100 (1 7)) VALUE(&DLSTIN)
0014.00 CHGDTAARA DTAARA(INVA100 (8 5)) VALUE(&DSLSTX)
0015.00 CHGDTAARA DTAARA(INVA100 (13 5)) VALUE(&DHNDCH)
0016.00
0017.00 ENDPGM
In this rapidly changing world of the AS/400, the only constant is change. In fact, it's not the
AS/400 anymore. So with topics like iSeries, Domino, Version 5, Websphere and Java
28
dominating the press, it is easy to loose track of the basic skills needed to survive and flourish
in the iSeries world. Here I present a small example of the power of Command Language (CL)
programming. This example is small enough to easily follow and yet it uses some of the less
obvious features of CL. Beginning programmers as well as system administrators can write
these simple programs and learn why experienced programs love the command language of
OS/400.
Parkinson's Law of Data is that data expands to fill the space available for storage. My corollary
to this law is that no matter how much disk storage you have, you only have 20% left. Here is a
simple and powerful tool to help discover where your disk space is going.
This tool consists of two small Command Language (CL) programs. These programs use two
features of CL that are somewhat hidden. These are:
· Files by size
· Files by owner
The first CL program will generate a list of all user libraries. The second program will read this
list and use it to build a composite list of all files on the system.
OS/400 keeps a description of every library and file. You probably know that these descriptions
are viewable using the Display Object Description (DSPOBJD) command. By changing the
OUTPUT option to *PRINT, you can print the descriptions. But what is less obvious is that by
changing the OUTPUT option to *OUTFILE, the command will create a database file as output.
Figure 1 shows the Command Language program to create a file listing all libraries on the
system. The operating system keeps a description of each library in the General Purpose
Library (QGPL). By using the DSPOBJD command with the *OUTFILE option, OS/400 will build a
database file that has a record for each library. The database file is named in the OUTFILE
parameter of the DSPOBJD command.
Entering the DSPOBJD command in Figure 1 is a little tricky. Key in the command DSPOBJD and
hit F4. Then fill in the values for the object and object type. For the OUTPUT parameter, use
*OUTFILE. When you hit enter, OS/400 will see that you are creating an *OUTFILE and will
display more keywords. Then you can fill in the OUTFILE keyword.
The program in Figure 1 does a little housekeeping first to delete the list of libraries from the
last time this program was run. The MONMSG command tells the program to continue if the file
does not already exist. Finally, the DSPOBJD command finds the descriptions of all user
libraries on the system and creates a file named ALLLIBS to hold the descriptions.
After running this program, you can examine the records in ALLLIBS. OS/400 has a database
format already defined to hold this information. In the file are fields to hold the specific
information about the library. Included in these fields are:
ODOBNM - the object name which in this case is the library name
29
ODOBSZ - the object size which in this case is the size of the library description
In Figure 2, the second line is the Declare File (DCLF) command. This is like a File Specification
in RPG. It tells the compiler to look for this file and to use the field names in the file as part of
the compiled object. In other words, the CL can reference any field in the file.
The next two lines of the program take care of housekeeping to delete the ALLFILES file. The
next executable statement is the Receive File (RCVF) command. This is the instruction to read
a record from the file. Since CL allows only one Declare File (DCLF) statement per program, the
RCVF command does not need to specify a file name. It reads the only one mentioned in the
DCLF command.
When the end of file is reached, the message CPF0864 will be sent. The Monitor Message
instruction, MONMSG MSGID(CPF0864) will realize that the end of file has been reached and will
go to the label EOJ.
Otherwise, the program will continue with the next statement which is a Display Object
Description (DSPOBJD) command. We used this command in the other program to get the
descriptions of all libraries. Here, we use it to get a list of all files in the first library. The
name of the library is in the field named ODOBNM. So, by using the field name &ODOBNM as
the value of the library and using *ALL as the value for the file, the command will get the
object descriptions of all files in the first library. By using the *OUTFILE option, we tell OS/400
to write the file descriptions to a file named ALLFILES.
The program then goes back to the statement labeled LOOP. It reads another record from the
ALLLIBS file. It gets a list of descriptions of all files from that library and adds them to
ALLFILES. It continues until it has added all descriptions of all files.
When the program is done, you have the file, ALLFILES which has the size, description, owner
and last date changed of every file on your system. To help manage your disk space you can
write queries to find files that should be deleted. You can look for files owned by programmers
that no longer work at your company. You can sort the files by last changed date to find files
that are no longer in use. You may want to make it a month end procedure to re-create
ALLFILES and then print out a summary of file space being used by different systems. In short,
you have a tool to help manage disk space.
--------------------------------------------------------------------------------
PGM
DLTF FILE(YOURLIB/ALLFILES)
MONMSG MSGID(CPF2105)
DSPOBJD OBJ(*ALLUSR) OBJTYPE(*LIB) OUTPUT(*OUTFILE) +
OUTFILE(YOURLIB/ALLLIBS)
30
ENDPGM
Figure 2 - This CL program reads the data in ALLLIBS and uses it to build ALLFILES.
PGM
DCLF FILE(YOURLIB/ALLLIBS)
DLTF FILE(YOURLIB/ALLFILES)
/* IF FILE DOES NOT EXIST, THAT'S OK */
MONMSG MSGID(CPF2105)
LOOP: /* READ A RECORD FROM "ALLLIBS" */
RCVF
/* IF END OF FILE IS REACHED, GO TO END */
MONMSG MSGID(CPF0864) EXEC(GOTO CMDLBL(EOJ))
DSPOBJD OBJ(&ODOBNM/*ALL) OBJTYPE(*FILE) +
OUTPUT(*OUTFILE) +
OUTFILE(YOURLIB/ALLFILES) OUTMBR(*FIRST +
*ADD)
/* IF LIBRARY HAS NO FILES, THAT'S OK */
MONMSG MSGID(CPF2123)
GOTO LOOP
EOJ:
Whether you are experienced programmer who is new to the AS/400 or you are completely new
to programming, you will save time and leverage your efforts by learning the basic skills of the
AS/400. In this series I have focused on the techniques that are both essential and unique to
the AS/400. The AS/400 has so much to offer that it is easy to lose sight of the basic skills that
provide the building blocks for all solutions.
I had a manager once who thought programmers made system design more complicated than it
needed to be. In his words, "all you do is read them, sort them and print them". He was
certainly right that putting the data in the right sequence is an essential technique to master.
The AS/400 has such a robust operating system that programmers often get to choose among
several approaches to solving problems. In the case of sequencing data, four different
approaches come to mind:
Use Query/400 to sort the data and create a copy of the original data in a new file.
Use the CL command, FMTDTA (format data) to sort the data and create a copy of the original
data in a new file.
Use the CL command OPNQRYF to read the data in the desired sequence.
The first approach, creating a logical file, is certainly effective. But each logical file that you
create must be maintained by the operating system each time a record is added or deleted to
the file. So, if you need the data resequenced only when a certain report is requested, you will
be adding unnecessary overhead to the system.
31
The second approach, Query/400, works well enough but creates a complete copy of the
original data. This makes it unnecessarily slow and requires extra disk space to run. The third
approach, FMTDTA, is rarely used. Like Query/400 it creates a complete copy of the original
data.
The Open Query File command. OPNQRYF, is a lot like creating a temporary logical file that
disappears when the job is done. It not only resequences the data but can easily select a subset
of the records. It resembles Structured Query Language (SQL) in structure. With it you can join
records from different files, group records together and even calculate new field values.
Consider a store sales evaluation system. Your user would like to list the sales in different
sequences. Also, the user sometimes wants to see the stores in only one state. Open Query File
is a great way to change the order of the data and select only certain records.
First, look at the very straightforward use of OPNQRYF to order the records by sales. That is,
list the store with the most sales first. There are five steps to using OPNQRYF:
Share the data path that you are building with the rest of the job
Call the program that uses the data from the OPNQRYF command
It's not quite as messy as it sounds. You can see the five steps in Figure 1. The first step is
always to use an override database file command, OVRDBF, to open the file with the
"SHARE(*YES)" option. Without this, the program will not use the new data path that is created
with the OPNQRYF command.
The next step in Figure 1 is the OPNQRYF command. Here, I am creating a new data path to the
store sales file named "SLS". Since I want to order the data by monthly sales, "SLMNTH", I have
used "SLMNTH" as the value for the "KEYFLD" parameter. Also, I want the largest sales first, so I
used the "*DESCEND" option.
Now, when the program calls the RPG program to print the report, it will read the "SLS" file
using this newly created data path. The program finishes with the housekeeping tasks of closing
the file and deleting the override.
Figure 2 is a CL program that will order the data by store number and print only stores in one
state. The state is specified as a parameter. Running the command, CALL SLSC101 'TX' will call
this program and print the stores in Texas in order by store number.
In Figure 2, notice the parameter "STATE" in the first line. This tells the program that a
parameter will be used when the program is called. The "DCL" statement tells the program that
the parameter will be a 2 byte field.
The "OPNQRYF" statement will use the "QRYSLT" parameter to select only records for stores in
Texas. If I always wanted the records for Texas I would simply code the parameter as:
QRYSLT('SLSTAT = "TX" ').
32
Since I want the user to specify the state as a parameter, I have added a statement to the CL
program to build the value for the "QRYSLT". I declared a variable named "QSELECT" and gave it
the value needed to select only records for the value used in the CALL statement.
Once you understand these simple examples, you can easily write a program to prompt the user
for the desired sequence and state. That program would can call a CL program similar to the
one in Figure 2 which will sort and select to right records.
These two examples will get you started with the power and flexibility of Open Query File.
--------------------------------------------------------------------------------
Figure 1 - This CL program uses Open Query File to order data by sales.
0001.00 PGM
0002.00
0003.00 /* Use OPNQRYF (Open Query File) to sequence store sales in */
0004.00 /* descending order */
0005.00
0006.00 /* 1st step is to share the data path with rest of job */
0007.00 OVRDBF FILE(SLS) SHARE(*YES)
0008.00
0009.00 /* 2nd step is the OPNQRYF command to build new path to SLS */
0010.00 /* using monthly store sales in descending order */
0011.00 OPNQRYF File((SLS)) KEYFLD((SLMNTH *DESCEND))
0012.00
0013.00 /* 3rd step is to call the RPG program that lists SLS file */
0014.00 CALL PGM(SLSR100)
0015.00
0016.00 /* 4th step is to close the file created in step 1 */
0017.00 CLOF OPNID(SLS)
0018.00
0019.00 /* 5th step is to delete the override from step 2 */
0020.00 DLTOVR FILE(SLS)
Figure 2 - This Open Query File CL uses a parameter to select data from only one
state.
33
0014.00 /* Order by SLSTOR (store#) - select only records for right STATE */
0015.00 OPNQRYF FILE((SLS)) QRYSLT(&QSELECT) KEYFLD((SLSTOR))
0016.00
0017.00 CALL PGM(SLSR100)
0018.00
0019.00 CLOF OPNID(SLS)
0020.00
0021.00 DLTOVR FILE(SLS)
The AS/400 has so many capabilities and options that it is easy to get overwhelmed with
choices. Learning the basic skills will help you apply your experience by using the built in
features of the AS/400. Each month, we will introduce you to essential AS/400 skills that are
powerful and easy to learn. Here we will look at the user friendly Screen Design Aid, SDA.
One of the most intuitive development tools on the AS/400 is SDA, Screen Design Aid. With it,
you build display screens and menus in a WYSIWYG (what you see is what you get) mode. We're
going to look at how easy it is to create a user menu.
Suppose a user says, "My department does only a few things on the AS/400 but the canned
software package that we have makes me navigate through too many screens. I'm training a
new hire and I wish you could make a special menu with only 6 selections:
1) Run the on-line Customer Maintenance program that is in our canned application
2) Submit the job to print all new orders
3) Run the query you wrote last week to display all back ordered parts
4) Run the query to print the customer service summary
5) Work with spool files to see if our reports are printing
6) Go to the Main "Customer Service Menu"
In only a few minutes, you can build a custom menu for your user. Start SDA with the command
STRSDA. You will see that SDA has three functions:
1) Design Screens
2) Design Menus
3) Test Display Files
You'll want to experiment with each of these later. For now, use selection 2 to design a menu.
Enter the name of your new menu, where you want to store the source (usually QDDSSRC) and
the library that it will be in. SDA will look for that menu and since it can't find it, it will create
it.
SDA creates a template for you that has the menu name and a list of ten selections. On this
screen, you list the selections for the user. On a separate screen, you list the commands
associated with each selection. Enter the prompts for your user, see Figure 1. Also, add a
selection 99 to signoff. Avoid keying in "insert" mode since that will shift all text on the screen.
To remove text, just space over it.
When you hit enter, SDA uses your screen to build literals in a display file. F20 provides a way
to display the text blocks that SDA will use. Each text block is preceded by a hidden character
that contains the display attribute for that string of text. If you key an "H" right in front of a
block of text, SDA will display that text in high intensity or white characters. An "R" will display
the text in reverse image. "D" will delete the text.
Next, define the commands for each selection. Hit F10 to enter the commands. You now can
key in the commands for selections 1 through 99.
34
Suppose your on-line Customer Maintenance program is run by calling the program, "CUSR000".
Tell SDA that you want selection 1 to "CALL CUSR000", see Figure 2. Suppose the job to print all
new orders is "ORDC110". You enter the command, "SBMJOB CMD(CALL ORDC110)" on line 2.
Now, when the user selects 2, the job "ORDC110" will be submitted to run as a batch job.
You can enter any command that you want the user to run. You can run queries, submit print
queries to run in batch and use standard utility commands such as "Work with Spool Files",
WRKSPLF. Finally, you can help the user navigate to another menu with the "GO" command
used for selection 6.
When you exit SDA, check the "object library" name. This is where your compiled menu objects
will go. Hit enter and the AS/400 will compile your source code and your menu will be ready to
run. SDA creates three objects, all with the same name: the commands, the display file and a
message file for the menu.
If you want some added fun, go back to the menu source using SDA and this time make the
selection to "work with menu help". Here you can define help screens that will display if your
user enters the selection number and hits F1.
Now you can tie this menu to your new employee's user profile. Use the work with user profile
command, WRKUSRPRF to change the new employee's profile. Put the new menu name in the
field, "first menu" at the bottom of the screen. Now, the user will see the MRCM001 menu when
he signs on the AS/400.
You can also use SDA to test and design display files. With the "Test Display Files" selection of
SDA, you can see the fields and presentation of a display file. This works even if you don't have
the source code.
The "Design Screens" selection of SDA is for building display files. You can reference the fields
in database files, move text and fields around the screen and modify the display attributes.
Knowing how to use SDA to design display files is an essential AS/400 skill but is beyond the
scope of this article. See IBM's "Screen Design Aid, User's Guide and Reference" for complete
coverage of SDA.
--------------------------------------------------------------------------------
35
99. Sign Off
Selection or command
Option Command
01 CALL CUSR000
02 SBMJOB CMD(CALL PGM(ORDC110))
03 RUNQRY QRY(PARQ020)
04 SBMJOB CMD(RUNQRY QRY(CSVQ100))
05 WRKSPLF
06 GO CSVM000
99 signoff
Change User
User . . . . . . . . . : JQSAMPLE
In this training track series I have presented basic skills that programmers and managers need
to be effective in the AS/400 world. This article will describe the steps for setting up a test
36
library and creating test database files. Having a good test bed of data is essential for
accurately testing program changes.
Building a proper test environment can be time consuming. The time is well spent though.
Aside from the obvious benefit of being able to test your program, you will get a clear picture
of the data and the relationships between files when you create your test data.
Figure 1 shows the steps to follow to set up a test environment. This may look like a lot of
work. The time spent setting up the test data will pay big dividends when you start testing. Not
only will you be able to test easily and quickly, you will be able to prove to your users and
quality assurance staff that your program is functioning correctly.
The perfect tool for isolating test data is Query/400. Query will not only find the data easily
but will also find the matching records in the associated files.
Typical of relational database environments, you cannot randomly select records from each of
these files. That is, the invoice records must belong to a customer record and the payment
records must belong to selected invoice records.
In this case, write a query to display invoices that are partially paid. From these, select several
and write down the customer numbers.
Query/400 is an excellent tool for selecting the data. Now write a query to select the INV
records for these customers and make a work file named INVTEST. Use the "Select Output Type"
option, to tell Query that you want to create a database file as output. Name the output file
INVTEST and store it in the test library, TEST1.
Write a similar query to select the PMT records for these customers. Name that file, PMTTEST.
Now, make a skeleton file definition for each of the files in your test library. There are several
ways to do this. My favorite method is to use the WRKOBJ command to find the file in my
production library and then to use selection 3 to copy the file to my test library.
37
For example, to copy the INV file definition from library DMCLIB, the command: WRKOBJ
DMCLIB/INV *FILE will show the INV in list format. Then, by keying "3" next to it, you can copy
the file definition to the test library, TEST1.
Repeat the process to copy the payments file. Remember that this builds a file with no records.
Similarly, copy the logical file definitions. If the logical file is named INV01, use the command:
WRKOBJ DMCLIB/INV01 *FILE to list the file and use the selection "3" to copy it to the test
library TEST1.
Repeat the process for each logical file that you need.
After you test your program, you will want to restore the data to its original state before
testing again. Write a simple CL program that consists of the two CPYF commands that you just
used to copy the test data to the test files. Once you compile this program, you can call the
program anytime you want reset your data.
Change your library list so the test library, TEST1 is before your other libraries. For most
environments, changing the current library to TEST1 is the way to do this. The command for
that is: CHGCURLIB TEST1
Confirm that you created the files in the TEST1 library. Use WRKOBJ to find all occurrences of
each file in your library list. For example: WRKOBJ INV *FILE will list all INV files in your library
list. The first one in the list should be in the TEST1 library. If not, stop! The whole point of
setting up this test environment is to ensure that the INV file used for testing is the test copy in
TEST1.
When you run your program, the AS/400 looks through your library list for each object that it
needs. If you compile your test program in TEST1, that is the one that will be called. Likewise,
when the program reads or updates data in the INV or PMT file, it will use the files that it finds
first. In this case, it finds the files in TEST1 and uses them.
In the case of the CUS file, the AS/400 will not find the CUS file in the TEST1 library. That
works fine because the CUS file is not updated by this program.
You can now test your program without harming any production data. You can also restore your
test data by calling the CL program that you just wrote.
38
We have followed some important principles of testing. These include:
· Have a defined test bed of data that can be restored
· Have test copies of all data that is modified
· Test the final version of the program. That is, do not construct a test that requires changing
the program after testing is complete. In other words, don't disable the UPDATE's in a program
so you can test without altering data.
· Keep all test objects in an isolated area.
You now have a seven step process to follow when testing. It requires no high level language
programming. You need only knowledge of a few commands and the ability to write simple
queries.
In this training track series I present essential AS/400 skills. Last month I discussed the design
of a simple sub-file display program. If you have lost track of last month’s article, e-mail me
for a copy. This article will expand that program to add the logic to process records selected by
the user.
AS/400 subfiles are equivalent to Windows list boxes. Getting comfortable with subfile
programming is essential for designing user friendly interfaces. Managers and business analysts
need to understand how to use this powerful technique. Using subfiles is an essential skill of
the AS/400 programmer.
Last month, I showed the code for a user to look up customers that belong to a salesperson.
Doing this is similar to running a query to display records in a file. While that is handy,
providing the ability to select records from that list is what makes subfile system design so
powerful.
With only a few instructions, the program can display more details about the customer using a
full screen display. A little more code will allow the user to update fields using that screen.
Finally, once the customer has been selected, the program can call another program that uses
a subfile to list the open orders for that customer.
39
First, the user must have a way of indicating which customer to select. There are advanced
techniques to select based on the cursor position. However, the easiest technique is to add a
one character field to the sub-file. The user simply keys X in the field next to the customer
number to select it.
I have added the field, SEL, to the subfile data record. I have also included a heading for it in
the subfile control record.
To see which record was selected, I added the subroutine SELRCD after the EXFMT operation.
Now, when a user keys an X on a line and hits ENTER, the subroutine can determine which
record was selected.
The AS/400 offers a simple and efficient way to read sub-file records. The READC operation
reads only records that have been changed. If the user keys a value in the SEL field, the READC
operation will read that record. It will automatically skip over any records that were not
selected.
So using the READC is an efficient way to look for selected records. Notice that if the user keys
spaces in SEL, the AS/400 sees those as changed records. Once the program reads a sub-file
data record, all the fields in that line are available to the program. So now, simply process the
customer selected.
In Figure 1 you can see the list of customers for salesperson # 6004 with a column for selection.
If the user keys X on the same line as Nick’s Deli, the READC operation in the subroutine
SELRCD will read that subfile data record. Then, the program can use that information to
display more detail about the customer.
It is almost as easy to use this interface to update the customer information. Just open the file
for update and add an UPDATE operation after the EXFMT. When you’ve done that, the user has
a friendly way to select a customer, see details from the customer master and change any
information that needs updating.
So what is involved in using this program to call another program to show the open orders for
the customer? Use the SELRCD routine to determine which customer was selected. Then,
instead of chaining to the customer file, call a new program with CUSNUM as a parameter.
That new program will use the CUSNUM filed to read all open orders for the customer and
display them in a subfile list. You could then add similar features to that subfile program to
display or change the details of the open orders. You could even let the user select an open
order and use that as a parameter to call a program to list the items in the order.
40
You can see where this is headed. Subfiles give you the ability to design robust, user friendly
programs. Combining these techniques with modular programming gives you the ability to build
very complicated systems, one simple step at a time.
--------------------------------------------------------------------------------
Salesperson# 6004
More..
F3=Exit
Figure 2: This is the DDS for the display file to select a customer from a list.
A REF(*LIBL/CUS)
A PRINT
A CA03(03)
*----------------------------------------------------------------
A R ENTSLS
*
* Let user enter a salesperson # so program can show a list
* of customers
A O 2 20'List Customers for Sls#'
A SLSPER R B 2 46EDTCDE(4)
*----------------------------------------------------------------
A R SFDATA SFL
A SEL 1A I 4 2
A CUSNUM R O 4 5
A CUSNAM R O 4 15
A CUSPHN R O 4 50
*----------------------------------------------------------------
A R SFCTL SFLCTL(SFDATA)
A SFLPAG(0015)
41
A SFLSIZ(0045)
A OVERLAY
A N50 SFLCLR
A 50 SFLDSP
A 50 SFLDSPCTL
A 50 SFLEND(*MORE)
A 2 4'Salesperson#'
A SLSPER R O 2 17EDTCDE(4)
A 3 1'Sel'
A 3 5'Cust'
A 3 15'Name'
A 3 50'Phone#'
*----------------------------------------------------------------
A R CMDKEYS
A 24 40'F3=Exit'
*----------------------------------------------------------------
Figure 3: This is the part of RPG subfile program to process the selected customer.
* Define the subfile and the variable that will hold record # (REC#)
FCUSD001 CF E WORKSTN
F SFILE(SFDATA:REC#)
FCUS IF E K DISK
*
C *IN03 DOWEQ *OFF
* Show screen for user to enter the salesperson's number
C EXFMT ENTSLS
C *IN03 IFEQ *OFF
* Clear the subfile
C EXSR CLRSF
* Load the subfile
C EXSR LODSF
* Show the command key footer and then the subfile SFCTL
C WRITE CMDKEYS
C EXFMT SFCTL
* Process any seleted records
C EXSR SELRCD
C ENDIF
C ENDDO
C MOVE *ON *INLR
C RETURN
*---------------------------------------------------------------------
C SELRCD BEGSR
* Read the changed subfile data records
C READC SFDATA 96
C *IN96 DOWEQ *OFF
C SEL IFEQ 'X'
* Process the customer
* either CHAIN to customer master and EXFMT a display screen
* or CALL a program with CUSNUM as a parameter
C ENDIF
C ENDDO
C READC SFDATA 96
C ENDSR
42
Subfile written in ILE:
43
0051.00 * !!!!! load 1 page of subfile (DO this # of times = to SFLPAG)
0052.00 C Do 5
0053.00 C Eval REC# = REC# + 1
0054.00 C Write SFDATA
0055.00 C CUSSLS ReadE CUS01 95
0056.00 C If *In95 = *on
0057.00 C Leave
0058.00 C EndIf
0059.00 C EndDo
0060.00 * If subfile is empty, write a record that says "*NO RECORDS"
0061.00 C If REC# = *zero
0062.00 C Eval REC# = REC# + 1
0063.00 C Clear SFDATA
0064.00 C Eval CUSNAM = '*NO RECORDS*'
0065.00 C Write SFDATA
0066.00 C EndIf
0067.00 C EndSr
0068.00 *----------------------------------------------------------------
0069.00 C SelectRecord BEGSR
0070.00 * Read the changed subfile data records
0071.00 C ReadC SFDATA 96
0072.00 C DoW *In96 = *off
0073.00 C If SEL = 'X' or SEL = '1'
0074.00 * Process the customer
0075.00 * either CHAIN to customer master and EXFMT a display screen
0076.00 * or CALL a program with CUSNUM as a parameter
0077.00 C EndIf
0078.00 C ReadC SFDATA 96
0079.00 C EndDo
0080.00 C EndSr
0081.00 *----------------------------------------------------------------
Experienced IS professionals can sometimes hack around in unfamiliar territory and solve
problems without completely understanding the details. But with an introductory knowledge of
the fundamentals of the AS/400 you can leverage your creative abilities and problem solving
skills. Each month, we will help you expand your AS/400 skills in only a few minutes. Whether
you are a manager who is spread too thin or a beginner with too much to learn in too little
time, you'll want to read this condensed overview of database files.
IBM introduced RPG-ILE in 1994 concurrent with release V3R1 of the OS/400 operating system.
You may have been scared away from trying RPG-ILE after reading articles about some of its
sophisticated features. Topics like activation groups, service programs and module binding may
have encouraged you to stay with the familiar RPG III. I'll show you how easy it is to convert
and compile your existing programs using RPG-ILE. You'll also see some great benefits from
simple additions in the language.
The simplest way to start using ILE is to convert an existing RPG III program. First, you will
need a place to put the new source code. The traditional name for an RPG III source file is
QRPGSRC. Similarly, the traditional name for an RPG-ILE source is QRPGLESRC.
Suppose there is RPG III source for a program XYZ in library PGMTEST. The source is in
QRPGSRC. Before you can convert the program to ILE, you must create a new physical source
file for it.
44
Do this with this command: CRTSRCPF FILE(PGMTEST/QRPGLESRC) RCDLEN(112)
It is important to specify the length. RPG-ILE source code uses records of 112 while
traditional RPG uses records of 96 characters.
To convert the program, use the convert RPG source command, CVTRPGSRC. It's this simple:
In a few seconds, the AS/400 will create a new member named XYZ in QRPGLESRC. This new
member should have RPGLE as its type.
If you've heard about complicated compiling and binding of modules, you will be presently
surprised to learn that you compile this source code in a familiar way. Just use option 14 in
PDM to compile the source. The AS/400 sees that the type is RPGLE and executes the
CRTBNDRPG command which compiles the program as an executable, stand-alone program.
It's that simple. You don't need to worry about activation groups, service programs or binding of
modules to compile and run RPG-ILE.
Figure 1 shows some code snippets from an RPG III program. These snippets include examples
of some of the changes in syntax that you will see with RPG-ILE. Figure 2 shows the RPG III
source code after it has been converted to RPG-ILE.
In the file specs, the overflow indicator for the printer is defined using the keyword OFLIND. In
the second file spec, the sub-file syntax has changed.
The E-specification has been replaced with the new D-specification. In this case, the array
definition has been replaced with the dimension statement, DIM(100).
The data structures in the I-specifications have also been replaced with D specs. The field
names in the D-spec can be 16 characters long. Not only that, you can indent fields to logically
group fields that belong together.
The DO and IF statements have not been changed noticeably by the conversion program. In a
moment, you'll see that the DO and IF statements have gained flexibility and readability in ILE.
The reference to the first element of the array now reads, ARA(1).
Since the operation field is larger in RPG-ILE, some operations are spelled completely. The old
operations UPDAT, SELEC and RETRN get spelled out as UPDATE, SELECT and RETURN.
You can see how easy it is to begin to use ILE. You may be asking the question, "Why bother?"
Certainly, converting to ILE without taking advantage of the flexibility of the new syntax isn't
advantageous.
Figure 3 shows the same code snippets rewritten using new ILE syntax. I've also added a
standalone field and a reference to a subroutine.
The most obvious difference is the use of upper and lower case characters. Most people keep
externally defined fields in all upper case. Temporary variables are most appropriately defined
45
in the D-specs. I defined the variable, AllRecsReadSw as a 1 character switch to indicate when
all records have been read. The "S" means this is a stand alone field. Defining variables in the
D-spec is considered good style and it allows you to use up to 16 characters for the field name.
I changed the data structure only slightly be indenting the fields that are subordinate to the
CCYY field.
The DO and IF statements are much easier to use and read. The Do-While-Equal syntax is
replaced with a Do-While statement. The conditional part of the statement is free format and
much easier to understand. The condition can use parentheses to remove any questions about
the intent of the logic.
To emphasize this, I coded a slightly complicated Do-While loop. It will execute the subroutine,
ReadAllCusts as long as the condition is true. The use of the parentheses makes the logical
evaluation of this statement unambiguous.
I changed the MOVE statement for the array to an EVAL statement. EVAL is intended to replace
Z-ADD, ADD and MOVE statements.
As you experiment with ILE you will discover a more flexible language than RPG III. Here are
some generally accepted styles of coding you should begin to use.
--------------------------------------------------------------------------------
46
F R1 KSFILE SFL1
* ARRAY
E AAA 100 5 A 100 ENTRIES OF 5
* DATA STRUCTURE
I DS
I 1 40CCYY
I 1 20CC
I 3 40YY
* DO AND IF STATEMENT
C *IN90 DOWEQ*OFF
C YY IFGT 80
C MM OREQ 12
C ADD 1 CC
C ENDIF
C ENDDO
* REFERENCE 1ST ELEMENT OF ARRAY
C MOVE *BLANKS ARA,1
D DS
D CCYY 1 4 0
D CC 1 2 0
D YY 3 4 0
* Stand Alone Field
D AllRcdsReadSw S 1
* Do and If statements
C DoW *in90 = *off
C If (YY > 80) or
47
C (MM = 12)
C Eval CC = CC + 1
C EndIf
C EndDo
* Reference 1st element of array
C Eval ARA(1) = *blanks
C DoW (AllRcdsReadSw = 'n') and
C ((*in90 = *off) or
C (*in91 = *on))
C ExSr ReadAllCusts
C EndDo
The oldest and obsolete method uses the RPG cycle and is not covered in this tutorial.
The next and most widely used is covered in this topic. The report is formatted using Output
Specs. These are identified with the letter O. Be sure to use F4 when entering O specs because
they have many options.
An O spec is written by using the EXCEPT operation. This comes from the history of RPG. When
using the RPG cycle, the O Specs are automatically written. When you don't use the RPG cycle,
the O Specs are written as an EXCEPTion to the cycle.
The F spec for the printer file QPRINT identifies an indicator which the AS/400 turns *ON when
the page is full.
The third way of printing will be covered in the next topic. It uses a printer file defined using
DDS specs.
This is the RPG program for writing a Print Report using O Specs. At the bottom, you will see a
sample of the report layout in the spool file.
48
0007.00 *----------------------------------------------------------------
0008.00 C* Print the heading
0009.00 C EXCPTHEADS
0010.00 C* Read the first record
0011.00 C READ CUST 90
0012.00 C*
0013.00 C *IN90 DOWEQ*OFF
0014.00 C* See if the page is full. If so, print the heading.
0015.00 C *IN10 IFEQ *ON
0016.00 C EXCPTHEADS
0017.00 C MOVE *OFF *IN10
0018.00 C ENDIF
0019.00 C* Print the detail
0020.00 C EXCPTDETAIL
0021.00 C READ CUST 90
0022.00 C ENDDO
0023.00 C*
0024.00 C MOVE *ON *INLR
0025.00 C RETRN
0026.00 *----------------------------------------------------------------
0027.00 OQPRINT E 202 HEADS
0028.00 O 6 'PAGE'
0029.00 C* PAGE is a system value that will keep track of the page#
0030.00 O PAGE 10
0031.00 O 47 'CUSTOMER ORDER REPORT'
0032.00 O 65 'DATE'
0033.00 C* UDATE is a system value that is today's date
0034.00 C* The edit code Y will edit the date like xx/xx/xx
0035.00 O UDATE Y 75
0036.00 O*
0037.00 O E 1 HEADS
0038.00 O 61 '# OPEN'
0039.00 O 71 'AMT OPEN'
0040.00 O*
0041.00 O E 1 HEADS
0042.00 O 17 'CUST #'
0043.00 O 23 'NAME'
0044.00 O 61 'ORDERS'
0045.00 O 70 'ORDERS'
0046.00 O*
0047.00 O E 1 DETAIL
0048.00 O CSNBR 17
0049.00 O CSNAME 49
0050.00 C* The edit code 1 will edit the amount like 1,234.50
0051.00 O CS#OPN1 60
0052.00 O CS$OPN1 70
****************** End of data ****************************************
Once you have written the program, compile it using option 14. After you have compiled the
program, you want to call it and see what the report looks like. To call the program, simply
type on the command line: CALL PGM(USER000/TUTROO5)
Next, go to the Work Spool File (WRKSPLF), find the program, and key in a 5 to view the
report.
49
To view the print report and programming code in RPG-ILE,
This is the RPG-ILE program for writing a Print Report using O Specs. At the bottom, you
will see a sample of the report layout in the spool file.
50
0043.00 O 61 'ORDERS'
0044.00 O 70 'ORDERS'
0045.00 O*
0046.00 O E Detail 1
0047.00 O CSNBR 17
0048.00 O CSNAME 49
0049.00 C* The edit code 1 will edit the amount like 1,234.50
0050.00 O CS#OPN 1 60
0051.00 O CS$OPN 1 70
****************** End of data ****************************************
Once you have written the program, compile it using option 14. After you have compiled the
program, you want to call it and see what the report looks like. To call the program, simply
type on the command line: CALL PGM(USER000/TUTROO5)
Next, go to the Work Spool File (WRKSPLF), find the program, and key in a 5 to view the
report.
As mentioned in the previous topic, there are 3 ways that reports get printed using RPG.
This topic will look at how to print using a print file defined with DDS specs.
In an earlier topic, we saw how to use DDS to define a physical file and a logical file. DDS is
also used to define print files and display files. A future topic will discuss display files.
A display and print file are different from a physical file in two ways:
1) A physical file can have only 1 record definition.
2) A print file and a display file are really defining different records, not data. I think of print
files as a buffer in memory that describes a print line.
To create the print file, STRPDM and add a member to QDDSSRC in your library. Make sure the
TYPE is PRTF.
Add a member named CUSW007. Enter the specs shown in the DDS attachment. Or if you are
lazy, copy it from library USER000.
51
0011.00 A 67PAGNBR
0012.00 A* SPACEA(2) MEANS TO SPACE DOWN 2 LINES AFTER THE HDG1 RECORD
0013.00 A SPACEA(2)
0014.00 A*----------------------------------------------------------------
0015.00 A R HDG2
0016.00 A SPACEA(1)
0017.00 A 48'# OPEN'
0018.00 A 59'AMT OPEN'
0019.00 A*----------------------------------------------------------------
0020.00 A R HDG3
0021.00 A SPACEA(1)
0022.00 A 2'CUST #'
0023.00 A 10'CUSTOMER NAME'
0024.00 A 48'ORDERS'
0025.00 A 60'ORDERS'
0026.00 A SPACEA(2)
0027.00 A*----------------------------------------------------------------
0028.00 A R DETAIL
0029.00 A SPACEA(001)
0030.00 A CSNBR 6S 0O 2
0031.00 A CSNAME 30A O 10
0032.00 A* THE EDTCDE(1) INDICATES TO EDIT THE NUMERIC FIELD USING
0033.00 A* THE SYSTME DEFINED CODE OF 1. THIS IS WITH COMMAS, DECIMALS
0034.00 A* AND NEGATIVE SIGN
0035.00 A CS$OPN R O 45EDTCDE(1)
0036.00 A CS#OPN R O 58EDTCDE(1)
0037.00 A*----------------------------------------------------------------
0038.00 A R TOTAL
0039.00 A SPACEA(001)
0040.00 A 30'TOTAL'
0041.00 A TOTOPN 9S 2O 58EDTCDE(1)
****************** End of data ****************************************
I have chosen to use the same field names in the print file as the input file that I will read. I
could have used different field names, but RPG handles fields with identical names in an
interesting way.
So, since we use the same values in the print file as the input, we need only read the input file
and write the print records. As before, we still need to take care of headings and page control.
To show you different techniques, I stated the length and type of CSNBR and CSNAME in the
DDS. For the other fields, I put in R in the REF column. This tells the compiler to look for the
field with this name in the file named at the top in the REF option. It gets the field type and
length from the file CUST since that is the file named in the REF parameter. Defined fields
using the REF technique are easier and preferred.
If you are familiar with RLU (Report Layout Utility), you could use it to create the DDS for the
print file. RLU is confusing to most AS/400 programmers and is not widely used.
When you have entered the DDS, use option 14 to compile it. Since the type is PRTF, the
AS/400 compiler knows to execute command CRTPRTF.
52
Then, enter the RPG or RPG-ILE source code and compile. Notice that the RPG programs use
the printer file as the output file.
Call the program from the command line and a print file will be created. Use WRKSPLF to see
it.
This is the RPG program for writing a Print Report using DDS. At the bottom, you will see a
sample of the report layout in the spool file.
Once you have written the program, compile it using option 14. After you have compiled the
program, you want to call it and see what the report looks like. To call the program, simply
type on the command line: CALL PGM(USER000/TUTROO7)
53
Next, go to the Work Spool File (WRKSPLF), find the program, and key in a 5 to view the
report.
This is the RPG program for writing a Print Report using DDS. At the bottom, you will see a
sample of the report layout in the spool file.
Once you have written the program, compile it using option 14. After you have compiled the
program, you want to call it and see what the report looks like. To call the program, simply
type on the command line: CALL PGM(USER000/TUTROO8)
Next, go to the Work Spool File (WRKSPLF), find the program, and key in a 5 to view the
report.
Appendix:
54
1.It is important to understand the history behind the RPG language. The language was
originally developed in the 1960's. It was a very high-level language meaning that a lot could be
done with few programming statements. It was originally intended to be used as a report
writer, hence the name Report Program Generator.
3 - Fixed column locations for values of each programming statement. That is, unlike
COBOL, C, Java, or Basic, the code is not free form.
4 - The use of Indicators or Switches was painfully required. A switch with a value of '0'
is off and a value of '1' is on. Switches were used to determine the results of I-O
operations and needed for simple compare instructions. For example, instead of a
statement that read like:
The programs were sometimes rather ugly. The use of indicators made many programs very
hard to understand.
RPG has evolved nicely to look more and more like other modern languages. While indicators
are still used for Input-Output (read and write) operations, standard conditional operations are
now available. IF/THEN, DoWhile, Do Until and Select Case operands exist. While still not
completely freeform, the statements have portions that are free form. Also, powerful string
handling functions like %trim, %subst, %len and %scan are available. If you are familiar the
concepts of structured programming you will see that RPG has everything required for
structured code.
So, while RPG is about 40 years old, its newest form (RPG IV) is modern and structured. The
problem is that many programs are still in use that have very old programming styles. So, you
really must be familiar with both RPG/400 (also known as RPG III) and RPG IV (also known as
RPG ILE).
RPG is a full featured language that can do all functions of data processing: reading files,
randomly retrieving data, navigating through indexes, reading and writing to data terminal or
display files, creating print reports and database files.
55
If you are still concerned that RPG is too old to be useful, consider that Visual Basic and ASP
are really just new versions of BASIC. BASIC is as old as RPG. Also, you should know that RPG
ILE can create compiled objects that bind with modules written in other languages (like C) and
can be used to process CGI scripts on the internet and output dynamic HTML. No kidding.
If you have an RPG book, I suggest you use it in addition to (or in place of) this tutorial. It is a
challenging task to describe such a complex language in short, readable topics.
To proceed you need to be familiar with PDM (see topic 4) and should have completed the
exercise in topic 5 that copies a file.
Since it important to know the old way of doing things (RPG/400) and the new way (RPG/ILE), I
will present each program we write in both languages. Learn the RPG/400 style so you can get
a job. Learn the RPG/ILE so you can see how modern the language has become.
Topic 5 showed you how to copy a file named CUST into your user library. Since this will be the
input file to our program, make sure that your library list includes your user library. I prefer to
have this library as my current library.
So, use DSPLIBL to display your library list. If you don't see your user library in the list, use the
CHGCURLIB USER999 command. This will change your current library to USER999. Remember
that when I say USER999, I mean your user ID.
If this doesn't show your file, either you didn't copy the file (see Topic 5) or your library list
does not include your user library. You should see that the address for every record is "123
MAIN STREET". We will write a program to read the first record of the file and change the
address to "456 OAK". In the next tutorial we will convert the program to RPG-ILE (RPG IV). In
later topics we will change the program to read every record in the file and change the address
to "456 OAK" in every record.
We need to enter program source statements, so start PDM (STRPDM). You should see
something like:
------------------------------------------------------------------------------------
AS/400 Programming Development Manager (PDM)
56
Type choices, press Enter.
Member:
Name . . . . . . . . . *ALL *ALL, name, *generic*
Type . . . . . . . . . *ALL *ALL, type, *generic*,
-------------------------------------------------------------------------------
Of course, change the USER999 to your user ID. Hit ENTER now.
If you get an error message that QRPGSRC was not found in USER999, you need to create the
file QRPGSRC to hold your RPG-III (RPG/400) source code. For a refresher on this, review the
topic on PDM. The command to create QRPGSRC in your library is:
File . . . . . . QRPGSRC
Library . . . . USER999 Position to . . . . .
Remember from your PDM lesson that this is trying to show you a list of all of the RPG programs
that you have entered. Since you haven't entered any, there is nothing to show.
You need to add a program. Let's call the program TUTR001. This name could be any 10
characters but if you don't follow a naming convention you will confused quickly. This name is
derived from TUT (for tutorial), R for RPG and 001.
Hit F6 to add the program. Key in the name (the source member), RPG for the source type and
a short description. Your screen should look like:
---------------------------------------------------------------------------
Start Source Entry Utility (STRSEU)
-----------------------------------------------------------------------
Now hit enter and you will see a blank screen where you can enter RPG source code.
57
Back in the seventies, programmers had to keep track of what column each operand of the RPG
statement was in. It was a mess.
With PDM (actually SEU, the Source Entry Utility), things are easier. Not easy, but easier. The
problem is that RPG is a fixed column language. It is not a free format language. SEU provides
prompting to help you but even it is a bit complicated at first.
The problem is that the different records types (F, I, E, C, O) do multiple things. That is, the I
record type can be used several different ways so it has several different ways of prompting.
The first statement of this program is a FILE specification. That is, it will name the file that we
will read and update. Since we want to read the CUST file, we will enter an F record to define
the file CUST.
To do this, hit F23. Since your PC keyboard doesn't have an F23 key, the Telnet5250 product
you are using has mapped the SHIFT-F11 key as an F23 key. So, hold down SHIFT and hit F11.
The top of your screen should look like:
Select Prompt
Wow. This means that there are 21 different formats to choose from. This is an easy one. Since
this is a FILE specification, we will use the F prompt. So key F for the Prompt and hit ENTER.
You should now see:
0001.00
****************** End of data ********************************
Prompt type . . . F Sequence number . . . 0001.00
File File
Continuation Exit Entry Addition Condition
What a mess! A couple of things have happened. The first is that the display shows your first
line of the program as line 0001.00. Not only that, the SEU editor has highlighted it because it
is blank, RPG-III does not allow blank lines! The rest of the screen shows all of the fields that
might be used to define a file. Don't panic yet. All you need to enter is:
58
I for File Type
F for File Designation
E for File Format
DISK for Device (2nd line from the bottom on the right)
Hit ENTER and your values will go in the right places. Amazingly, the one value that you can't
enter here is the F that tells RPG that this is a FILE specification. So hit enter, your screen
looks like:
FMT F .....FFilenameIPEAF....RlenLK1AIOvKlocEDevice+
*************** Beginning of data ************
0001.00 CUST IF E DISK
****************** End of data ***************
FMT F .....FFilenameIPEAF....RlenLK1AIOvKlocEDevice+
*************** Beginning of data ************
0001.00 FCUST IF E DISK
****************** End of data ***************
Now, calm down. This may be the only time in your life that you actually key in a FILE
specification. In the world of programming, everything is copied and changed, cut and paste.
Normally, you will simple copy an existing program and then change and add statements to it.
So, what did you just define? You told the program that you will be using the file CUST, that
the file is being used for Input (you will read the file), that the file is Fixed length (almost all
files are fixed length), that the file is Externally defined (that is, it was defined by the
database language of the AS/400) and finally that the file is a DISK file (not a PRINTER or
Display Screen).
Imagine for a moment a program where the user enters a customer number on the display
screen. The program reads the customer record, looks up the customer's most recent order and
prints it. Such a program will have at least 4 files: Display screen file (a WORKSTN file)
Customer file (a DISK file) Order file (a DISK file) Printer file (a PRINTER file)
Now we are ready for the processing logic of the program. The total logic of this program will
be only 6 statements. Processing logic in RPG is known as CALCULATION statements and uses
the C specification and the C prompt type.
Soon, you will get more comfortable with PDM and SEU. Now, we need to add a line to after
the F statement. Do this by keying an I (for insert) on top of the 0001.00.
FMT FX .....FFilenameIPEAF........L..I........Device+
*************** Beginning of data ************
I001.00 FCUST IF E DISK
59
When you hit ENTER, the editor will create a blank line for you and even place your cursor in
column 6 where you can enter the C for CALCULATION. With the cursor on the line with the C,
hit Shift-F11 to prompt and this time use C for the prompt type.
Your screen will now show:
Decimal
Length Positions H/N/P HI LO EQ Comment
Now finally some good news. Most of the RPG programming you do is using the C specification.
This type of statement is not too messy.
The first processing step of the program is to read the first record of the CUST
file. RPG needs to know 2 things about a read statement:
Indicators are numbered from 01 to 99. There are some other special ones too.
FMT FX .....FFilenameIPEAF........L..I........Device+......KExit++En
*************** Beginning of data ***************************
0001.00 FCUST IF E DISK
0002.00 C READ CUST 90
****************** End of data ******************************
60
There are 3 places to enter indicators for statements. Unfortunately, it is not always easy to
figure out which place to use. The HI, LO and EQ headings come from the obsolete way
indicators were first used. In old RPG, there were no IF statements. Instead, you used a COMP
(Compare) statement to compare the values of FACTOR1 and FACTOR2 and then would set on
an indicator if FACTOR1 was higher than FACTOR2, a different indicator if FACTOR1 was lower
that FACTOR2 and finally an indicator if they were equal. We will NEVER do this.
The lesson here is simple. For a READ instruction, the indicator mentioned in the EQ field will
be turned on when there are no more records in the file.
Let's pause for a minute and look at all that is happening with these 2 statements.
The F specification tells the AS400 to set up a way to read the CUST file. It also tells it to find
the fields and attributes of the fields using the database definition of the CUST file. In most
other languages, you would need a statement to OPEN the file. Such a statement tells the
computer that you are ready to use the file. RPG assumes that you want to read the file and
automatically opens the file for you. There is a simple way to tell the AS400 that you want to
OPEN and CLOSE the file explicitly but there is no reason to do that.
The READ statement reads the first record of the file CUST and populates an area in your
program's memory with the data that is in the first field. You program has each field defined
and now has data in each field.
After the READ, the field CSNBR has a value of 1002, the field CSNAME has a value of E
LUMPKIN, the field CSADR1 has a value of 123 MAIN STREET.
We now want to change the value in CSADR1. Unlike other languages, if we move only a few
characters to a field that is 30 characters long, RPG will change only the first 3 characters. So
we will first blank out the field and then move 456 OAK to the address.
Insert a line for a new C statement. One way to do this is to key an I over the
0002.00 and hit ENTER. Then key C in column 6 and hit Shift-F11. Key C for prompt
type and hit ENTER. We want to blank out the CSADR1 field so we will move blanks
to it. RPG has some system values defined that are named like:
*BLANK or *BLANKS
*ZERO or *ZEROES
*ON or *OFF
*IN90 (for indicator 90)
As you might guess, we want to move the value *BLANK to CSADR1. So fill in your screen to look
like:
61
0002.00 C READ CUST 90
0003.00 C MOVE *BLANK CSADR1
****************** End of data ********************************
Now insert another line to MOVEL '456 OAK' to CSADR1. This says to move the characters
in between the quotes to the field named CSADR1. The operand is MOVEL for MOVE LEFT.
This means that since '456 OAK' is only 7 characters and CSADR1 is 30 characters,
move '456 OAK' to the left
At this moment, the data on the disk has the value '123 MAIN STREET' in the area of
the record for CSADR1. If you ran this program, at this spot in the program, the
value of the field CSADR1 in memory would be '123 OAK'. But if you stopped here,
the new value ('123 OAK') would not get stored in the file. To do that you must
UPDATE the record. In RPG-III verbs or operands can be only 5 characters so the
verb is UPDAT. Also, you must use the record name for the file. This is too
complicated to explain here but trust me that the record name for the CUST file
is CSREC.
So add the statement to update the record CSREC. Now that we see that we are using
the file for Update (not just Input), it is time to change the I in the FILE
definition from I to U.
First, we have to discuss a housekeeping item left over from the very early days
of RPG. We know that RPG will automatically open the file. However, it will only
close the file if the "LAST RECORD" indicator is ON. For now, just make sure the
last 2 statements to execute in a program are:
MOVE *ON *INLR
RETRN
62
0006.00 C MOVE *ON *INLR
0007.00 C RETRN
Now that you are finished, hit F3 and ENTER to save your statements.
So you now have the source statements to compile an RPG program. You compile it just like
you compiled the CL program. Key a 14 on the line next to the program name in PDM and hit
ENTER. Like:
File . . . . . . QRPGSRC
Library . . . . USER999 Position to . . . . .
By the way, this program is in the USER000 library. You can see it using PDM.
After compiling your program, you will want to look at the compiled listing. Remember, you do
this with the command:
WRKSPLF
Device or Total
Opt File User Queue User Data Sts Pages
TUTR001 DAVIDMOUNT PRTDGM RDY 5
TUTR001 DAVIDMOUNT PRTDGM RDY 5
Put your cursor next to the last one and key in a 5 and hit ENTER.
You will see your compiler listing. If you key in a B at the top and hit ENTER, the AS400 will
take you to the bottom of your listing. If your compile worked, it will look like:
Final Summary
Message Count: (by Severity Number)
TOTAL 00 10 20 30 40 50
2 2 0 0 0 0 0
Program Source Totals:
Records . . . . . . . . . . : 7
Specifications . . . . . . : 7
Table Records . . . . . . . : 0
Comments . . . . . . . . . : 0
63
PRM has been called.
Program TUTR001 is placed in library USER000. 00 highest severity.
***** END OF COMPILATION ****
The phrase "Program TUTR001 is placed in library USER???" means the AS400 understood your
statements well enough to crate an executable program.
If your compile didn't work, start by making sure that you have your user library in your library
list. The easy way to do this is:
CHGCURLIB USER999
All that is left is to run the program. There are several ways to do this but the most
straightforward way is to use this command:
CALL TUTR001
The program will run almost instantly. Then if you look at the CUST file the address of the first
record will have changed:
RUNQRY QRYFILE(CUST)
---------------------------------------------------------------------------------------------------------------
A REF(CUST)
A CA03(03 'F3=EXIT')
A R SCR1
A 1 2USER
A O 1 28'ADD/UPDATE/DELETE/INQUIRE'
A DSPATR(HI)
A 1 72DATE
A EDTCDE(Y)
A 2 2'CUSTR01'
A 2 72TIME
A O 6 8'(A)dd, (U)pdate, (D)elete'
A O 6 34'(I)nquire, (N)ext'
A ACTION 1 B 6 53DSPATR(HI)
A O 8 28'Cust#'
A CUS# R B 8 35DSPATR(HI) EDTCDE(4)
A ERRLIN 78 O 22 2
A 90 DSPATR(RI)
A O 23 14'F3=EXIT'
A*----------------------------------------------------------------
A R SCR2
A CA09(09 'F9=DELETE')
A 1 2USER
A 1 28'CUSTOMER FILE MAINTENANCE'
A 1 72DATE
A EDTCDE(Y)
A 2 2'CUSTR01'
64
A MODE 7 O 2 37
A 2 72TIME
A 3 28'Cust#'
A CUS# R O 3 37EDTCDE(4) DSPATR(RI)
A 8 28'Name'
A CUSNAM R B 8 42
A N80 DSPATR(HI)
A 80 DSPATR(PR)
A 9 28'Address 1'
A CUSAD1 R B 9 42
A N80 DSPATR(HI)
A 80 DSPATR(PR)
A 10 28'Address 2'
A CUSAD2 R B 10 42
A N80 DSPATR(HI)
A 80 DSPATR(PR)
A 11 28'City'
A CUSCTY R B 11 42
A N80 DSPATR(HI)
A 80 DSPATR(PR)
A 12 28'State'
A CUSSTA R B 12 42
A N80 DSPATR(HI)
A 80 DSPATR(PR)
A 13 28'Zip'
A CUSZIP R Y B 13 42
A N80 DSPATR(HI)
A 80 DSPATR(PR)
A ERRLIN 78 O 22 2
A 90 DSPATR(RI)
A O 23 14'F3=EXIT'
---------------------------------------------------------------------------------------------------------------------
b)This is the RPG III source code for the Add/Change/Delete program named CUSTR01.
---------------------------------------------------------------------------------------------------------------------
FCUSTD01 CF E WORKSTN
FCUST UF E K DISK A
*----------------------------------------------------------------
* Define error messages here
I 'RECORD ALREADY ON FI-C ERR1
I 'LE'
I 'RECORD IS NOT ON FIL-C ERR2
I 'E'
I 'NO MORE RECORDS' C ERR3
I 'ZIP CANNOT BE ZERO' C ERR6
I 'NAME MUST NOT BE BL-C ERR7
I 'ANK'
I 'STATE MUST NOT BE BL-C ERR8
I 'ANK'
I 'ACTION MUST BE A, U,-C ERR9
65
I ' D OR I'
I 'RECORD ADDED SUCCE- C MSG1
I 'SSFULLY'
I 'RECORD UPDATED SUC- C MSG2
I 'CESSFULLY'
I 'RECORD DELETED SUC- C MSG3
I 'CESSFULLY'
I 'HIT F9 TO DELETE' C MSG4
I 'NO ACTION TAKEN' C MSG9
*----------------------------------------------------------------
* Key List for customer file
* (a key list is not needed since this file has only 1 key
* field, but this makes the program easier to modify if
* the file has multiple key fields)
C KEYLST KLIST
C KFLD CUS#
*----------------------------------------------------------------
* Stay in Main DO LOOP until F3 is hit from SCR1
*----------------------------------------------------------------
C *IN03 DOWEQ*OFF
* Show main ADD/UPD/INQ/DLT screen
C EXFMTSCR1
C CLEARERRLIN
C MOVE *OFF *IN90
* If user didn't hit F3, process screen based on action
C *IN03 IFEQ *OFF
C SELEC
C ACTION WHEQ 'A'
C EXSR ADDREC
C ACTION WHEQ 'D'
C EXSR DLTREC
C ACTION WHEQ 'I'
C EXSR INQREC
C ACTION WHEQ 'N'
C EXSR NXTREC
C ACTION WHEQ 'U'
C EXSR UPDREC
C OTHER
C CLEARERRLIN
C MOVELERR9 ERRLIN
C MOVE *ON *IN90
C ENDSL
C ENDIF
C ENDDO
*
C MOVE *ON *INLR
C RETRN
*----------------------------------------------------------------
C ADDREC BEGSR
* Unprotect fields but setting *in80 off
C MOVE *OFF *IN80
C MOVE ' ADD ' MODE
* See if customer # is already in CUST file
C KEYLST CHAINCUST 91
C *IN91 IFEQ *OFF
66
* If customer # is already in cust file, load up ERR MSG
C CLEARERRLIN
C MOVELERR1 ERRLIN
C MOVE *ON *IN90
C ELSE
C EXSR ADDSCR
C ENDIF
C ENDSR
*----------------------------------------------------------------
* SHOW ADD SCREEN
*----------------------------------------------------------------
C ADDSCR BEGSR
* Clear customer record except for key field(s)
C *NOKEY CLEARCUSTR
C MOVE 'N' RECOK 1
* Keep showing screen until record passes edit or F3 is hit
C RECOK DOWEQ'N'
C *IN03 ANDEQ*OFF
C EXFMTSCR2
*
C *IN03 IFEQ *OFF
C EXSR EDTCUS
C RECOK IFEQ 'Y'
C WRITECUSTR
* Show messaage that ADD was successful
C CLEARERRLIN
C MOVELMSG1 ERRLIN
C ENDIF
C ELSE
* Show message that no action was taken because F3 was hit
C CLEARERRLIN
C MOVELMSG9 ERRLIN
C ENDIF
C ENDDO
*
C MOVE *OFF *IN03
*
C ENDSR
*----------------------------------------------------------------
C DLTREC BEGSR
* Disable fields for entry by setting *IN80 on
C MOVE *ON *IN80
C MOVE 'DELETE' MODE
C CLEARERRLIN
C MOVELMSG4 ERRLIN
C MOVE *ON *IN90
* See if customer # is in CUST file
C KEYLST CHAINCUST 91
C *IN91 IFEQ *ON
* If customer # is not in CUST file, show ERR MSG
C CLEARERRLIN
C MOVELERR2 ERRLIN
C MOVE *ON *IN90
C ELSE
C EXFMTSCR2
67
C MOVE *OFF *IN90
C *IN09 IFEQ *ON
C DELETCUSTR
C CLEARERRLIN
C MOVELMSG3 ERRLIN
C ELSE
C CLEARERRLIN
C MOVELMSG9 ERRLIN
C ENDIF
C ENDIF
*
C MOVE *OFF *IN03
*
C ENDSR
*----------------------------------------------------------------
C INQREC BEGSR
* Disable fields for entry by setting *IN80 on
C MOVE *ON *IN80
C MOVE 'INQUIRY' MODE
* See if customer # is in CUST file
C KEYLST CHAINCUST 91
C *IN91 IFEQ *ON
* If customer # is not in CUST file, show ERR MSG
C CLEARERRLIN
C MOVELERR2 ERRLIN
C MOVE *ON *IN90
C ELSE
* Show Inquiry Screen
C EXFMTSCR2
C ENDIF
*
C MOVE *OFF *IN03
*
C ENDSR
*----------------------------------------------------------------
C NXTREC BEGSR
*
C MOVE *ON *IN80
C MOVE 'INQUIRY' MODE
C CLEARERRLIN
* Go forward in the file to the next record
C KEYLST SETLLCUST 92 93
* If *in92 is on, we have reached end of file
C *IN92 IFEQ *ON
C MOVELERR3 ERRLIN
C MOVE *ON *IN90
C ENDIF
* Read next record
C ERRLIN IFEQ *BLANKS
C READ CUST 90
* If *in90 is on, we have reached end of file
C *IN92 IFEQ *ON
C MOVELERR3 ERRLIN
C MOVE *ON *IN90
C ENDIF
68
C ENDIF
* If *in93 is on, we are at an existing record and we need
* to read past it
C ERRLIN IFEQ *BLANKS
C *IN93 IFEQ *ON
C READ CUST 90
* If *in90 is on, we have reached end of file
C *IN90 IFEQ *ON
C MOVELERR3 ERRLIN
C MOVE *ON *IN90
C ENDIF
C ENDIF
C ENDIF
*
C ERRLIN IFEQ *BLANKS
C EXFMTSCR2
C ENDIF
*
C MOVE *OFF *IN03
*
C ENDSR
*----------------------------------------------------------------
C UPDREC BEGSR
* Unprotect fields but setting *in80 off
C MOVE *OFF *IN80
C MOVE 'UPDATE ' MODE
* See if customer # is already in CUST file
C KEYLST CHAINCUST 91
C *IN91 IFEQ *ON
* If customer # is not in cust file, load up ERR MSG
C CLEARERRLIN
C MOVELERR2 ERRLIN
C MOVE *ON *IN90
C ELSE
C EXSR UPDSCR
C ENDIF
C ENDSR
*----------------------------------------------------------------
* SHOW UPDATE SCREEN
*----------------------------------------------------------------
C UPDSCR BEGSR
C MOVE 'N' RECOK 1
* Keep showing screen until record passes edit or F3 is hit
C RECOK DOWEQ'N'
C *IN03 ANDEQ*OFF
C EXFMTSCR2
*
C *IN03 IFEQ *OFF
C EXSR EDTCUS
C RECOK IFEQ 'Y'
C UPDATCUSTR
* Show messaage that UPDATE was successful
C CLEARERRLIN
C MOVELMSG2 ERRLIN
C ENDIF
69
C ELSE
* Show message that no action was taken because F3 was hit
C CLEARERRLIN
C MOVELMSG9 ERRLIN
C ENDIF
C ENDDO
*
C MOVE *OFF *IN03
*
C ENDSR
*----------------------------------------------------------------
C EDTCUS BEGSR
* First, set error switch that record is OK
C MOVE 'Y' RECOK
* Now, perform each edit
C CUSNAM IFEQ *BLANKS
C MOVE 'N' RECOK
C CLEARERRLIN
C MOVELERR7 ERRLIN
C MOVE *ON *IN90
C ENDIF
*
C CUSSTA IFEQ *BLANKS
C MOVE 'N' RECOK
C CLEARERRLIN
C MOVELERR8 ERRLIN
C MOVE *ON *IN90
C ENDIF
*
C CUSZIP IFEQ *ZERO
C MOVE 'N' RECOK
C CLEARERRLIN
C MOVELERR6 ERRLIN
C MOVE *ON *IN90
C ENDIF
*
C ENDSR
c). This is the RPG IV source code for the Add/Change/Delete program named CUSTR01.
---------------------------------------------------------------------------------------------------------------------
FCUSTD01 CF E WORKSTN
FCUST UF A E K DISK
*---------------------------------------------------------------------
D RecOK S 1a
*---------------------------------------------------------------------
D Err1 C CONST('RECORD ALREADY ON FI-
D LE')
D Err2 C CONST('RECORD IS NOT ON FI-
D LE')
D Err3 C CONST('NO MORE RECORDS')
D Err4 C CONST('ZIP CAN NOT BE BLANK')
D Err5 C CONST('NAME IS MANDATORY')
D Err6 C CONST('STATE IS MANDATORY')
70
D Err7 C CONST('INVALID ACTION')
D Err8 C CONST('NO MORE RECORDS')
D Msg1 C CONST('RECORD ADDED')
D Msg2 C CONST('RECORD UPDATED')
D Msg3 C CONST('RECORD DELETED')
D Msg4 C CONST('HIT F9 TO DELETE')
D Msg9 C CONST('NO ACTION TAKEN')
*---------------------------------------------------------------------
C KEYLST KLIST
C KFLD CSNBR
C*---------------------------------------------------------------------
C* Display the add, delete, inquire, next, or update screen
C* and process unless user hit F3 (indicator 03)
C
C DoW *in03 = *off
C ExFmt SCR1
C Eval ERRLIN = *blanks
C Eval *in90 = *off
C If *in03 = *off
C Select
C When Action = 'A'
C ExSr AddRecord
C When Action = 'D'
C ExSr DltRecord
C When Action = 'I'
C ExSr InqRecord
C When Action = 'N'
C ExSr NextRecord
C When Action = 'U'
C ExSr UpdRecord
C Other
C Eval ERRLIN = Err7
C Eval *in90 = *on
C EndSl
C EndIf
C EndDo
C
C Eval *inlr = *on
C Return
C*---------------------------------------------------------------------
C AddRecord BegSr
C
C* Indicator 80 is used by the display file to protect most fields
C* since we are in ADD mode, set the indicator off to allow field entry
C Eval *IN80 = *off
C Eval MODE = ' ADD'
C* See if customer is already on file. If so, display error
C KEYLST Chain CUST 91
C If *in91 = *off
C Eval ERRLIN = Err1
C* Indicator 90 draws attention to the error line with reverse display
C Eval *in90 = *on
C Else
C ExSr AddScreen
C EndIf
71
C
C EndSr
C*---------------------------------------------------------------------
C AddScreen BegSr
C
C* Clear all fields except the key field
C *NOKEY Clear CSREC
C Eval RecOK = 'n'
C* Stay on this screen until user gets it right or hits F3
C Dow RecOK = 'n' and
C *in03 = *off
C ExFmt SCR2
C If *in03 = *off
C ExSr EditRecord
C If recOK = 'y'
C Write CSREC
C Eval ERRLIN = Msg9
C EndIf
C Else
C Eval ERRLIN = Msg9
C EndIf
C EndDo
C
C Eval *in03 = *off
C
C EndSr
C*---------------------------------------------------------------------
C DltRecord BegSr
C
C* Indicator 80 is used by the display file to protect most fields
C* since we are in DLT mode, set the indicator on for no field entry
C Eval *in80 = *on
C Eval MODE = 'DELETE'
C* Display "Hit F9 to DELETE" in ERRLIN
C Eval ERRLIN = Msg4
C Eval *in90 = *on
C* See if customer is on file. If not, show error Msg
C KEYLST Chain CUST 91
C If *in91 = *on
C Eval ERRLIN = Err2
C Else
C* If customer is on file, show screen again and see if user hit F9
C* to confirm delete
C ExFmt SCR2
C Eval *in90 = *off
C If *in09 = *on
C Delete CSREC
C Eval ERRLIN = Msg3
C Else
C Eval ERRLIN = Msg9
C EndIf
C EndIf
C
C Eval *in03 = *off
C EndSr
72
C*---------------------------------------------------------------------
C InqRecord BegSr
C
C* Indicator 80 is used by the display file to protect most fields
C* since we are in DLT mode, set the indicator on for no field entry
C Eval *in80 = *on
C Eval MODE = 'INQUIRY'
C
C KEYLST Chain CUST 91
C If *in91 = *on
C Eval ERRLIN = Err2
C Eval *In90 = *on
C Else
C ExFmt SCR2
C EndIf
C Eval *in03 = *off
C
C EndSr
C*---------------------------------------------------------------------
C NextRecord BegSr
C
C Eval *in80 = *on
C Eval MODE = 'INQUIRY'
C Eval ERRLIN = *blanks
C
C* Set file cursor at cust from screen
C KEYLST SetLL CUST 92 93
C If *in92 = *on
C Eval ERRLIN = Err3
C Eval *in90 = *on
C EndIf
C
C* Read file to get next customer
C If ERRLIN = *BLANKS
C Read CUST 90
C If *in92 = *on
C Eval ERRLIN = Err3
C Eval *in90 = *on
C EndIf
C EndIf
C* If *in93 is on, we are at an existing record and need to read past it
C If *in93 = *on and
C ERRLIN = *blanks
C Read CUST 90
C If *in90 = *on
C Eval ERRLIN = Err3
C Eval *in90 = *on
C EndIf
C EndIf
C
C If ERRLIN = *blanks
C ExFmt SCR2
C EndIf
C Eval *in03 = *off
C
73
C EndSr
C*---------------------------------------------------------------------
C UpdRecord BegSr
C
C Eval *IN80 = *off
C Eval MODE = ' UPDATE '
C KEYLST Chain CUST 91
C If *in91 = *on
C Eval ERRLIN = Err2
C Eval *in90 = *on
C Else
C ExSr UpdScreen
C EndIf
C
C EndSr
C*---------------------------------------------------------------------
C UpdScreen BegSr
C Eval RecOK = 'n'
C DoW RecOK = 'n' and
C *in03 = *off
C ExFmt SCR2
C If *in03 = *off
C ExSr EditRecord
C If RecOK = 'y'
C Update CSREC
C Eval ERRLIN = Msg2
C EndIf
C Else
C Eval ERRLIN = Msg9
C EndIf
C EndDo
C Eval *in03 = *off
C EndSr
C*---------------------------------------------------------------------
C EditRecord BegSr
C Eval RecOK = 'y'
C
C If CSNAME = *blanks
C Eval RecOK = 'n'
C Eval ERRLIN = Err5
C Eval *in90 = *on
C EndIf
C
C If CSSTE = *blanks
C Eval RecOK = 'n'
C Eval ERRLIN = Err6
C Eval *in90 = *on
C EndIf
C
C If CSZIP = *zero
C Eval RecOK = 'n'
C Eval ERRLIN = Err4
C Eval *in90 = *on
C EndIf
C
74
C EndSr
---------------------------------------------------THE END----------------------------------------------------
75