Starter Kit- Chapter 25 System iNetwork (formerly iSeries Network)
Home Site Map Contact Us My Profile Log In Join Now!
Info Centers
 Forums

 Tech Center

 News & Analysis

 Solution Center

 UK Centre
Popular Spots
 25th Anniversary

 Article Archive

 ProVIP Center (Club Tech)

 Code

 System i DocFinder

 Essential Guides

 Blogs

 Wikis

 e-Learning

 Webcasts

 Podcasts

 System i Jobs

 Events
Products
 i5 Route Finders

 Learning Center (Store)

 Product Directory
Network Poll
Determining a programmer's desktop requirements is not a black-and-white proposition, but matching equipment to programmer type can help productivity. Which "programmer type" are you?
Vote Now!
Network Memberships
 See Membership Levels

 Free E-Mail Newsletters

 Free RSS Feeds

 Subscribe/Join

 Upgrade Now

 Renew Now
About Us
 About the Network

 Network Publications

 Tech Editor Profiles

 Editorial Calendar

 Contact Us

 Subscribe

 Media Kit (PDF)

 Write For Us


System iNetwork March Sponsor





        System iNetwork March Sponsor


Home » Starter Kit » TOC » Chapter 25
  AS/400-iSeries Starter Kit


Chapter 25 - CL Programming: The Classics

Since the inception of CL on the S/38 in the early eighties, programmers have been collecting their favorite and most useful CL techniques and programs. Over time, some of these have become classics. In this chapter, we'll visit three timeless programs and five techniques essential to writing classic CL.

The five techniques:

  • Error/exception message handling
  • String manipulation
  • Outfile processing
  • IF-THEN-ELSE and DO groups
  • OPNQRYF (Open Query File) command processing

When I consider the CL programs I would label as classic, I find these techniques being employed to some degree.

You may recognize the classic programs we'll visit as similar to something you have created. They provide functions almost always needed and welcomed by MIS personnel at any AS/400 installation. If you are new to the AS/400, I guarantee you will get excited about CL programming after you experience the power of these tools. And if you are an old hand at CL, you may have missed one of these classics.

These programs are useful and the techniques valid on the S/38 as well, although some of the details will be different (e.g., the syntax of qualified object names and some outfile file and field names).

Classic Program #1: Changing Ownership

If you ever face the problem of cleaning up ownership of objects on your system, you will find the CHGOBJOWN (Change Object Owner) command quite useful. You will also quickly discover that this command works for only one object at a time. Let's see . . . that means you must identify the objects that will have a new owner and then enter the CHGOBJOWN command for each of those objects. Or is there another way? When the solution includes the repetitious use of a CL command, you can almost always use a CL program to improve or automate that solution. To that end, try this first classic CL program, CHGOWNCPP.

CHGOWNCPP demonstrates three of the fundamental CL programming techniques: message monitoring, string handling, and outfile processing. Let's take a quick look at how the program logic works and then examine how each technique is implemented.

Program Logic. When you execute the command CHGOWN
(Figure 25.1a), it invokes the command-processing program CHGOWNCPP (Figure 25.1b). A program-level message monitor traps any unexpected function check messages caused by unmonitored errors during program execution. If it encounters an unexpected function check message, the MONMSG (Monitor Message) command directs the program to continue at the RSND_LOOP label.

The CHKOBJ (Check Object) command verifies that the value in &NEWOWN is an actual user profile on the system. If the CHKOBJ command can't find the user profile on the system, a MONMSG command traps CPF9801. If this happens, an escape message is then sent to the calling program using the SNDPGMMSG command, and the CPP terminates.

The DSPOBJD (Display Object Description) command generates the outfile QTEMP/CHGOWN based on the values for variables &OBJ and &OBJTYPE received from command CHGOWN. The program then processes the outfile until message CPF0864 ("End of file") is issued.

For each record in the outfile, the CPP executes a CHGOBJOWN command to give ownership to the user profile specified in variable &NEWOWN. The variables &ODLBNM and &ODOBNM contain the object's library and object name, obtained from fields in the outfile file format QLIDOBJD. The value in variable &CUROWNAUT specifies whether the old owner's authority should be revoked or retained. When the CHGOBJOWN command is successful, the program sends a completion message to the calling program's message queue and reads the next record from the file. If the CHGOBJOWN command fails, the error message causes a function check, and the program-level message monitor passes control to the RSND_LOOP label. (Note: The CUROWNAUT parameter does not exist on the S/38 CHGOBJOWN command, so you would need to eliminate it, along with variable &CUROWNAUT in CHGOWNCPP.)

After all records have been read, the next RCVF command generates error message CPF0864, and the command-level message monitor causes the program to branch to the FINISH label. The RSND_LOOP label is encountered only if an unexpected error occurs. This section of the program is a loop to receive the unexpected error messages and resend them to the calling program's message queue.

The Technique

Message Monitoring. The first fundamental technique we will examine is error/exception message handling. Monitoring for system messages within a CL program is a technique that both traps error/exception conditions and directs the execution of the program based on the error conditions detected. The CL MONMSG command provides this function. Program CHGOWNCPP uses both command-level and program-level message monitoring.

A command-level message monitor lets you monitor for specific messages that might occur during the execution of a single command. For instance, in program CHGOWNCPP, MONMSG CPF9801 EXEC(DO) immediately follows the CHKOBJ command to monitor specifically for message CPF9801 ("Object not found"). If CPF9801 is issued as a result of the CHKOBJ command, the message monitor traps the message and invokes the EXEC portion of the MONMSG command -- in this instance, a DO command.

Another example in the same program is the MONMSG command that comes immediately after the RCVF statement. If the RCVF command causes error message CPF0864, the message monitor traps the error and invokes the EXEC portion of that MONMSG -- in this instance, GOTO FINISH.

What happens if an error occurs on a command and there is no command-level MONMSG to trap the error? If there is also no program-level MONMSG for that specific error message, the unexpected error causes function check message CPF9999, and if no program-level MONMSG for CPF9999 exists, the program ends in error.

A program-level message monitor is a MONMSG command placed immediately after the last declare statement in a CL program. In our program example, there is a program-level MONMSG CPF9999 EXEC(GOTO RSND_LOOP). This MONMSG handles any unexpected error since all errors that are unmonitored at the command level eventually cause a function check. For instance, if the CHGOBJOWN command fails, an error message is issued that then generates function check message CPF9999. The program-level MONMSG traps this function check, and the EXEC command instructs the program to resume at label RNSD_LOOP and process those error messages.

For more information on monitoring messages, see IBM's AS/400 manual Programming: Control Language Programmer's Guide (SC41-8077), or Appendix E of the AS/400 manual Programming: Control Language Reference (SC41-0030).

String Handling. Another fundamental technique program CHGOWNCPP employs is string manipulation. The program demonstrates two forms of string handling -- substring manipulation and concatenation. The first is the %SST (Substring) function. (%SST is a valid abbreviated form of the function %SUBSTRING -- both perform the same job.) The %SST function, which returns to the program a portion of a character string, has three arguments: the name of the variable containing the string, the starting position, and the number of characters in the string to extract.

For instance, when the command CHGOWN passes the argument &OBJ to the CL program, the variable exists as a 20-character string containing the object name in positions 1 through 10 and the library name in positions 11 through 20. The CL program uses the %SST function in the CHGVAR (Change Variable) command (A in Figure 25.1b) to extract the library name and object name from the &OBJ variable into the &OBJNAM and &OBJLIB variables.

The second form of string handling in this program is concatenation. The control language interface supports three distinct, built-in concatenation functions:

  • CAT (||): Concatenate - concatenates two string variables end to end;
  • *TCAT (|<): Trim and concatenate -- concatenates two strings after trimming all blanks off the end of the first string;
  • *BCAT (|>): Blank insert and concatenate - concatenates two strings after trimming all blanks off the end of the first string and then adding a single blank character to the end of the first string.

To see how these functions work, let's apply them to these variables (where /b designates a blank):

&VAR1 *CHAR 10
VALUE('John/b/b/b/b/b') and
&VAR2 *CHAR 10 VALUE('Doe/b/b/b/b/b/b/b')

The results of each operation are as follows:

&VAR1 || &VAR2 = John/b/b/b/b/b/bDoe
&VAR1 |< &VAR2 = JohnDoe
&VAR1 |> &VAR2 = John Doe

The SNDPGMMSG command (B in Figure 25.1b) uses concatenation to build a string for the MSGDTA (Message Data) parameter. Notice that you can use a combination of constants and program variables to construct a single string during execution. The only limitation is that variables used with concatenation functions must be character variables because they will be treated as strings for these functions. You must convert any numeric variables to character variables before you can use them in concatenation.

If the variables &ODLBNM, &ODOBNM, and &NEWOWN in the SNDPGMMSG command contain the values MYLIB, MYPROGRAM, and USERNAME, respectively, the SNDPGMMSG statement generates the message "Ownership of object MYLIB/MYPROGRAM granted to user USERNAME."

Outfile Processing. The final fundamental technique demonstrated in program CHGOWNCPP is how to use an outfile. You can direct certain OS/400 commands to send output to a database file instead of to a display or printer. In this program, the DSPOBJD command generates the outfile QTEMP/CHGOWN. This file contains the full description of any objects selected.

The file declared in the DCLF (Declare File) command is QADSPOBJ, the system-supplied file in library QSYS that serves as the externally defined model for the outfile generated by the DSPOBJD command. (Note: To get a list of the model outfiles provided by the system, you can execute the command "DSPOBJD QSYS/QA* *FILE".) Because file QADSPOBJ is declared in this program, the program will include the externally defined field descriptions when you compile it, allowing it to recognize and use those field names during execution.

The next step in using an outfile in this program is creating the contents of the outfile using the DSPOBJD command. DSPOBJD uses the object name and type passed from command CHGOWN to create outfile QTEMP/CHGOWN. The outfile name is arbitrary, so I make a practice of giving an outfile the same name as the command or program that creates it.

The program then executes the OVRDBF (Override with Database File) command to specify that the file QTEMP/CHGOWN is to be accessed whenever a reference is made to QADSPOBJ. This works because QTEMP/CHGOWN is created with the same record format and fields as QADSPOBJ. Now when the program reads record format QLIDOBJD in file QADSPOBJ, the actual file it reads will be QTEMP/CHGOWN.

These three fundamental CL techniques give you a good start in building your CL library, and the "Change Owner of Object(s)" tool is definitely handy. You may have discovered the CHGLIBOWN (Change Library Owner) tool in library QUSRTOOL. This IBM-provided tool offers a similar function.

Classic Program #2: Delete Database Relationships

The second utility is a real timesaver: the "Delete Database Relationships" tool provided by command DLTDBR and CL program DLTDBRCPP. DLTDBR uses the same three fundamental techniques described above and adds a fourth: the IF-THEN clause. Let's take a quick look at the program logic and then discuss the IF-THEN technique.

Program Logic. When you execute command DLTDBR (Figure 25.2a), the command-processing program DLTDBRCPP (Figure 25.2b) is invoked. As in CHGOWNCPP, a program-level MONMSG handles unexpected errors.

The DSPDBR (Display Database Relations) command generates an outfile based on the file you specify when you execute the command DLTDBR. The CPP then processes this outfile until message CPF0864 ("End of File") is issued.

For each record in the outfile, the program performs two tests as decision mechanisms for program actions. Both tests check whether or not the record read is a reference to a physical file (&WHRTYP = &PFTYPE). If the file is not a physical file, the program takes no action for that record; it just reads the next record. The first test (A in Figure 25.2b) determines whether dependencies exist for this physical file. &WHNO represents the total number of dependencies. If &WHNO is equal to zero, there are no dependencies for this file, and the program sends a message (using the SNDPGMMSG command) to that effect.

The second test (B) checks whether &WHNO is greater than zero. If it is, the record represents a dependent file, and you can delete the file name specified in variables &WHRELI (dependent file library) and &WHREFI (dependent file name) with the DLTF (Delete File) command.

When the DLTF is successful, the program sends a completion message to the calling program's message queue. The GOTO RCD_LOOP command sends control to the RCD_LOOP label to read the next record. If the DLTF command fails, the error message causes a function check, and the program-level message monitor directs the program to resume at the RSND_LOOP label.

After all records have been read, the RCVF command generates error message CPF0864, and the command-level message monitor causes the program to branch to the FINISH label, where the program ends. As with the first program, you will encounter the RSND_LOOP label only if an unexpected error occurs.

The Technique

IF-THEN-ELSE and DO Groups. The IF-THEN clause lets you add decision support to your CL coding via the IF command, which has two parameters: COND (the conditional statement) and THEN (the action to be taken when the condition is satisfied). A simple IF-THEN statement would be

  IF COND(&CODE = 'A') THEN(CHGVAR VAR(&CODE) VALUE('B'))

In this example, if the value of variable &CODE is A, the CHGVAR command changes that value to B. To create code that is easier to read and interpret, it is usually best to omit the use of the keywords COND and THEN. The above example is much clearer when written as

  IF (&CODE = 'A') CHGVAR VAR(&CODE) VALUE('B') 

Conditions can also take more complex forms, such as

  IF ((&CODE = 'A' *OR &CODE = 'B') *AND (&NUMBER = 1))  +
GOTO CODEA

This example demonstrates several conditional tests. The *OR connective requires at least one of the alternatives -- (&CODE = 'A') or (&CODE = 'B') -- to be true to satisfy the first condition. The *AND connective then requires that (&NUMBER = 1) also be true before the THEN clause can be executed. If both conditions are met, the program executes the GOTO command. (For more information about how to use *AND and *OR connectives, see Chapter 2 of the AS/400 manual Programming: Control Language Programmer's Guide.)

The ELSE command provides additional function to the IF command. Examine these statements:

IF  (&CODE = 'A') CALL PGMA
ELSE              CALL PGMB

The program executes the ELSE command if the preceding condition is false.

You can also use the IF command to process a DO group. Examine the following statements:

  IF (&CODE = 'A') DO
  CALL PGMA
  CALL PGMB
  CALL PGMC
ENDDO

If the condition in the IF command is true, the program executes the DO group until it encounters an ENDDO.

The DO command also works with the ELSE command, as this example shows:

 IF (&CODE = 'A') DO
  CALL PGMA
  CALL PGMB
ENDDO
ELSE DO
   CALL PGMD
   CALL PGME
   CALL PGMF
ENDDO

For more information about IF and ELSE commands, see the AS/400 manual Programming: Control Language Reference, (SC41-0030) or the Programming: Control Language Programmer's Guide.

Classic Program #3: List Program-File References

The last of the classic CL programs and fundamental techniques, the "Display Program References" tool, brings us face-to-face with one of the most powerful influences on CL programming -- the one and only OPNQRYF (Open Query File) command. As this program demonstrates, this classic technique is one of the richest and most powerful tools available through CL. Let's take a quick look at the program logic for this tool, provided via the LSTPGMREF (List Program References) command and the LSTPRCPP CL program. Then we can take a close look at the OPNQRYF command.

Program Logic. When you execute command LSTPGMREF
(Figure 25.3a), the command-processing program LSTPRCPP
(Figure 25.3b) is invoked. LSTPRCPP uses the DSPPGMREF (Display Program References) command to generate an outfile based on the value you entered for the PGM parameter. The outfile LSTPGMREF then contains information about the specified programs and the objects they reference.

Notice that this program does not use the DCLF statement. There is no need to declare the file format because the program will not access the file directly. You will also notice that the program uses the OVRDBF command, but the SHARE(*YES) parameter has been added. Because a CL program cannot send output to a printer, LSTPRCPP must call a high-level language (HLL) program to print the output. The OVRDBF is required so the HLL program, which references file QADSPPGM, can find outfile QTEMP/LSTPGMREF. The override must specify SHARE(*YES) to ensure that the HLL program will use the Open Data Path (ODP) created by the OPNQRYF command instead of creating a new ODP and ignoring the work the OPNQRYF has performed. Files used with OPNQRYF require SHARE(*YES).

After the DSPPGMREF command is executed, file LSTPGMREF contains records for program-file references as well as program references to other types of objects. The next step is to build an OPNQRYF selection statement in variable &QRYSLT that selects only *FILE object-type references and optionally selects the particular files named in the FILE parameter. LSTPRCPP uses IF tests to construct the selection statement. Then the CPP determines the sequence of records desired (based on the value entered for the OPT parameter in the LSTPGMREF command) and uses the OPNQRYF command to select the records and create access paths that will allow the HLL program to read the records in the desired sequence.

The CL program then calls HLL program LSTPRRPG to print the selected records (I haven't provided code here -- you will need to build your own version based on your desired output format). The outfile will appear to contain only the selected records, and they will appear to be sorted in the desired sequence.

The Technique

The OPNQRYF Command. Without doubt, one of the more powerful commands available to CL programmers is the OPNQRYF command. OPNQRYF uses the same system database query interface SQL uses on the AS/400. The command provides many functions, including selecting records and establishing keyed access paths without using an actual logical file or DDS. These two basic functions are the bread-and-butter classic techniques demonstrated in program LSTPRCPP.

Record selection is accomplished with OPNQRYF's QRYSLT parameter. If you know the exact record selection criteria when you write the program, filling in the QRYSLT parameter is easy, and the selection string will be compiled with the program. But the real strength of ONPQRYF's record selection capability is that you can construct the QRYSLT parameter at runtime to match the particular user requirements specified during execution. Program LSTPRCPP demonstrates both the compile-time and runtime capabilities of OPNQRYF.

When you write program LSTPRCPP, the requirement to include only references to physical files is a given. Therefore, you can use the statement CHGVAR VAR(&QRYSLT) VALUE('WHOBJT = "F"') to initially provide a value for &QRYSLT to satisfy that requirement. The &FILE value is unknown until execution time, so the code must allow this selection criteria to be specified dynamically. First, what are the possible values for the FILE parameter on command LSTPGMREF?

  • You may specify a value of *ALL. If you do, you should not add any selection criteria to the QRYSLT parameter. The &QRYSLT value would be
      'WHOBJT = "F"' 
  • You may specify a generic value, such as IC* or AP??F*. If you enter a generic value, the CL program must determine that &FILE contains a generic name and then use OPNQRYF's %WLDCRD (wildcard) function to build the appropriate QRYSLT selection criteria. The %WLDCRD function lets you select a group of similarly named objects by specifying an argument containing a wildcard (e.g., * or ?). For instance, if you wanted to select all files beginning with the characters IC, you would use the argument IC*. An example of the &QRYSLT variable for this generic selection would be
      'WHOBJT = "F" *AND WHFNAM = %WLDCRD("IC*")' 
  • You may specify an actual file name. If you do, the CL program must first determine that fact and then simply use the compare function in OPNQRYF to build the value for the QRYSLT parameter. An example for this &QRYSLT variable would be
      'WHOBJT = "F" *AND WHFNAM = "FILE_NAME"

Examining the program, you will see that it performs a series of tests on the variable &FILE to determine how to build the QRYSLT parameter. If *ALL is the value for &FILE, all other IF tests are bypassed, and the program continues. If the program QCLSCAN finds the character * in the string &FILE, it uses the %WLDCRD function to build the appropriate QRYSLT parameter. If the program does not find *ALL and does not find a * in the name, the value of &FILE is assumed to represent an actual file name, and the program compares the value of &FILE to the field WHFNAM for record selection. Obviously, the power of the QRYSLT parameter is in the hands of those who can successfully build the selection value based on execution-time selections.

The second basic bread-and-butter technique is using OPNQRYF to build a key sequence without requiring additional DDS. Program LSTPRCPP tests the value of &OPT to determine whether the requester wants the records listed in *FILE (file library/file name) or *PGM (program library/program name) sequence. The appropriate OPNQRYF statement is executed based on the result of these tests (see A in Figure 25.3b).

When &OPT is equal to &FILESEQ (which was declared with the value F), the OPNQRYF statement sequences the file using the field order of WHLNAM (file library), WHFNAM (file name), WHLIB (program library), WHPNAM (program name). When &OPT equals &PGMSEQ (declared with the value P), the key fields are in the order WHLIB, WHPNAM, WHLNAM, WHFNAM. No DDS is required. The HLL program called to process the opened file can provide internal level breaks based on the option selected.

For more information concerning the use of the OPNQRYF command with database files, refer to IBM's Programming: Control Language Reference or Programming: Data Base Guide (SC41-9659).

Classic CL programs and techniques are a part of the S/38 and now AS/400 heritage, but they're not simply oldies to be looked at and forgotten. Studying these programs and mastering these techniques will help you hone your skills and write some classic CL code of your own.


Starter Kit for the AS/400, 2nd Edition
Copyright 1994 by Duke Press
DUKE COMMUNICATIONS INTERNATIONAL
Loveland, Colorado

All rights reserved. No part of this book may be reproduced in any form by any electronic or mechanical means (including photocopying, recording, or information storage and retrieval) without permission in writing from the publisher.

It is the reader's responsibility to ensure procedures and techniques used from this book are accurate and appropriate for the user's installation. No warranty is implied or expressed.

This book was printed and bound in the United States of America.
Second Edition: April 1994

ISBN 10882419-09-X



  Sponsored Links   Featured Links


Penton Technology Media
Connected Home | SQL Server Magazine | Windows IT Pro
Report Bugs | Contact Us | Comments/Suggestions | Terms of Use | Privacy Statement | Trademarks
See Membership Levels | Subscribe | Free E-mail Newsletters | Free RSS Feeds | My Profile | Upgrade Now | Renew Now

© 2010 Penton Media, Inc.
Penton Media
System i is a trademark of International Business Machines Corporation and is used by Penton Media, Inc., under license. SystemiNetwork.com is published independently of International Business Machines Corporation, which is not responsible in any way for the content. Penton Media, Inc., is solely responsible for the editorial content and control of the System iNetwork.