navigation graphic


PHP Resources RPG Resources Debugging Resources RDi Resources Windows 10 & IBM i CL Resources Our Videos Hot Tips!



CL Enhancements

In this article, we would like to discuss the enhancements made to CL programming over the last few releases of the IBM i Operating System.

Ever since the AS400 launched in 1988, right through to V5R3 in 2004, IBM had made no enhancements to CL programming. We thought we were burdened with the same CL forever! We were stuck with restrictions like, only being able to open one file in a program, cannot use printer or ICF files, use sub-files within display files, nor use program described display files.

Then IBM got very busy on the CL front, and we now have many great features.

New Variable Types

As can be seen from the figure below, we can now define variables as Integer and un-signed Integer.

Integer & UnSigned Integers

Dcl &Count *Int Dcl &Days *UInt

Another one of the first enhancements, was the ability to use up to, and including, five files in a program. In the figure below, we can see two files being declared and then used in a program.

Multiple Files

/* 5 Files can be declared */ DclF UtJrn01p OpnId( Journal ) DclF UtRst01p OpnId( Restore ) RcvF Journal RcvF Restore Clof Journal /* Now close the Journal file */

This enhancement saved us having to have a one file, one CL program restriction, but the main enhancement, in our opinion, was the addition of structured programming commands.

Now we are really talking about a programming language!

Let us take a look at each of these structured commands in detail.

Looping Commands

The DOFOR command is basically a For Loop. It will execute commands a number of times.


/* Do 10 times */ DoFor Var(&Count) From(1) To(10) ChgVar &Msg %Bin(&Rcv &Count 2) SndPgmMsg &Msg ToPgmQ(*Ext) EndDo

In the above figure, we can see the DOFOR loop will increment the count variable and execute the send program message command ten times.

In other programming languages, the DOFOR command behaves in a similar fashion to a FOR command.

The DOUNTIL command performs loop processing until a condition is reached. The DOUNTIL command always executes the DO loop at least once.


/* Do Until 100 Times */ DoUntil Cond( &Count *Gt 100 ) ChgVar &Count (&Count + 1 ) ChgVar &Msg %Bin(&Rcv &Count 2) SndPgmMsg &Msg ToPgmQ( *Ext ) EndDo /* Checking if count is greater than 100 is done here! */

The DOWHILE is very similar to the DOUNTIL command, except this one will check the condition before the loop is performed, not after!


/* Do While 100 Times */ DoWhile Cond( &Count *Lt 100 ) ChgVar &Count (&Count + 1 ) ChgVar &Msg %Bin(&Rcv &Count 2) SndPgmMsg &Msg ToPgmQ( *Ext ) EndDo

The difference between a DOUNTIL and a DOWHILE loop always seems to pop up at interviews!

The ITERATE command interrupts the processing of a DO loop and passes control to the associated ENDDO command. This is shown in the figure below.


/* Iterate Example */ DoWhile Cond( &Count *Lt 100 ) If (&Msg = '*END') Iterate ChgVar &Count (&Count + 1 ) ChgVar &Msg %Bin(&Rcv &Count 2) SndPgmMsg &Msg ToPgmQ( *Ext ) EndDo /* Iterate goes down to here! */


The SELECT command is used in a block to perform conditional processing of groups. It is used in conjunction with the WHEN and OTHERWISE commands.


/* Select Groups */ Select When Cond( &Count *Lt 100 ) Then( Do ) ChgVar &Count (&Count + 1 ) ChgVar &Msg %Bin(&Rcv &Count 2) SndPgmMsg &Msg ToPgmQ( *Ext ) EndDo When Cond( &Count *Eq 100 ) Then( Do ) ChgVar &Count ( &Count - 1 ) EndDo OtherWise Do SndPgmMsg &Msg ToPgmQ(*Ext) EndDo EndSelect

The LEAVE command drops out of a DO loop.


/* Leave Do Group Example */ DoWhile Cond( &Count *Lt 100 ) If (&Msg = '*END') Leave /* Get out of the Do Loop */ Chgvar &Count 10 EndDo

As you can see in the above examples, we have to code ENDDO/ENDSELECT to mark the end of either a DO or SELECT structured block.

Then in 2006, IBM kindly gave us V5R4, and more improvements!


This time we had subroutines and pointer type variables.

For those who are not aware of what a subroutine is, subroutines give us the ability to name a section of our program, then reuse it as many times as we like.


/* Call SubRoutine */ CallSubR SubR( RtvSplfNbr ) RtnVal( &SplfNbr2 ) Return /* All our subroutines now ..... */ SubR SubR(RtvSplfNbr) ChgVar &BytesAvail 70 ChgVar &FmtName 'SPRL0100' ChgVar &ErrorCode x'0000000000000000' ChgVar &SplfExists '1' Call QSprIlSp (&RcvVar &RcvVarLen &FmtName + &ErrorCode) MonMsg Cpf333a exec(ChgVar &SplfExists '0') If (&SplfExists) Then Else Do ChgVar &SplfNbr 0 EndDo EndSubR RtnVal( &SplfNbr )

As we can in the above figure, a subroutine starts with the SUBR command and end it with ENDSUBR. All the code in between is our subroutine.

To call this subroutine we use the CALLSUBR anywhere in our CL program.


In 2008, 6.1 came along, this time we had only one major update, the INCLUDE command.

Programmers of other languages will again be familiar with this command as it copies in, at compile time, source from another source member.


/* + FormaServe Systems Ltd + + © - FormaServe Systems Ltd 1990 - 2011 + */ Start: Pgm Dcl &FulJob *Char 26 Dcl &Job *Char 10 Dcl &Nbr *Char 6 Include ErrorC1 /* Include Error Handling Variables */ ChgVar &Msg ('Error Example for ....') RtvJobA Job(&Job) User(&User) Nbr(&Nbr) Return: Return Include ErrorC2 /* Include Error Handling Variables */

If we look at the compiler list, we can now see the entire source from Error1 include at compile time.


Then in 2010, we had 7.1, and IBM added support for built in functions to work with character strings. I am sure our RPG developers will recognise these.

Please note, PTF SI49061 must be loaded and applied in order to use these new CL built-in functions.

Let us go through each of these.


The Check functions, %CHECK and %CHECKR, give us the ability to check if certain characters appear, or not, in a string.

%CHECK will let you know the first position of a string that does not exist in the compare string.

Example 1

/* Check if * in String + String = '*******' */ ChgVar &Len Value(%Check('*' &String)) /* &LEN will have the value 2 */ /* Where is the first non CR character from right? + Text = ' £10,000 CR' Sign = 'CR' */ ChgVar &Len Value(%CheckR(&Sign &Text)) /* &LEN will have the value 9 */

The %CHECKR (Check Reverse) function will inform you of the last position of the string that does not appear.

Trim Functions

Trim functions, which perform like the RPG equivalent, will trim, or strip, characters from a string.

There are %TRIML (Trim character from the left only),
%TRIMR (Trim characters from the right only)
and %TRIM (Trim characters from both left and right).

Example 1

/* &Amount is ' *** 10.55 *** ' */ ChgVar &Result Value( %Trim( &Amount ' *' ) ) /* &Result will now be '10.55' + The leading blanks & asterisks are removed */

Scan Functions

Also introduced was the Scan Function %SCAN. This command gave us the ability to search for a string within another string. If it does not not find it, %SCAN returns zero.


/* &Text is 'CL now supports %CHECK, %CHECKR and %SCAN' */ ChgVar Var(&Pos) Value(%Scan('%' &Text)) /* &Pos value is 17 + The first occurrence of '%' within the variable &Text */

Onto the latest release, 7.2. Once again, in this release, IBM focused on the built in functions to enhance CL.

String Handling

The %LOWER function will convert any upper-case characters to lower-case.

The %UPPER function will convert any low-case characters to upper-case.

Both of these functions are demonstrated in the figure below.

Example 1

/* &CoLower is 'formaserve' */ ChgVar &String Value( %Upper( &CoLower ) ) /* &String will now be 'FORMASERVE' */ /* &CoUpper is 'FORMASERVE' */ ChgVar &String Value( %Lower( &CoUpper ) ) /* &String will now be 'formaserve' */

The %CHAR function, converts a non-character to a character. For example, from an integer to a character string as seen below.


/* &Count is 50 */ ChgVar &String Value( %Char( &Count ) ) /* &String will now be '50' */

The %DEC, as I’m sure you can guess, converts a non-numeric to a numeric.

The %INT function converts a value to an integer.

The %UINT function will convert a value to an unsigned integer.

Next, we have two new functions for the sizing of variables.

The %LEN function will inform us of the number of digits, or characters, in a variable.

The %SIZE function returns the number of bytes occupied by a variable.

Both the %LEN and %SIZE functions are presented below.

%Size %Len Examples

/* &Count is 50 */ ChgVar &String Value( %Char( &Count ) ) /* &String will now be '50' */ /* &CoLower is defined as *Char 50 + with a value of 'formaserve ' */ ChgVar &Len Value( %Len( &CoLower ) ) /* &Len will be 50 as it contains text & blanks */ /* &CoLower is defined as *Char 50 */ ChgVar &Size Value( %Size( &CoLower ) ) /* &Size will be 50 */

For those who think the above two functions seem the same, the difference between %LEN and %SIZE, is that %SIZE is calculated at compile time and %LEN is calculated at run time.


These are just some of the major enhancements to CL programming, visit the IBM information centre for full details.

This topic was presented at the International i Power Conference 2015 in the UK by Andy Youens. To download the slides from this presentation, click here ...

  follow us on twitter   find us on facebook   follow us on linkedin   see our videos   find us on google+