Fri, Jul
4 New Articles

The CL Corner: A CL-Based Implementation of RPG Built-in %Check

  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Find characters that don't belong!


I recently received the following from Wayne R.: "I use RPG's %CHECK function to check if any characters are not valid. Is there something similar in CL?" RPG's %Check built-in, if you're not familiar with it, returns the first position in a variable that contains a character that is not in a list of valid characters. If, for instance, you had a variable with a value of 'cabbage', then comparing this to a list of valid values such as 'abc' would return a position value of 6. This is due to the sixth character (the "g") not being in the list of valid values ('abc'). If all characters in the variable are found in the valid character list, %Check returns a position value of 0. While CL currently does not have a built-in such as RPG's %Check, you will see it's not that difficult to create a CL command to provide this function for programming our IBM i—and in fact that's what we'll do in today's article.


The CHKCHR Command

The command we will create is Check Characters (CHKCHR), and this is the source for the command:

Cmd       Prompt('Check Characters')                    

Parm       Kwd(Comparator) Type(*Char) Len(192) Min(1) +

             Prompt('Valid characters')                  

Parm       Kwd(Base)       Type(*Char) Len(1024) Min(1) +

             Prompt('String to be checked')              

Parm       Kwd(Pos)       Type(*UInt4)         Min(1) +

             RtnVal(*Yes) +                              

             Prompt('Pos of first invalid character')    

The CHKCHR command is loosely patterned after the RPG %Check built-in. The first parameter, Comparator, is a character variable set to the characters that represent "valid" characters; it maps to the first parameter of the %Check built-in. I rather arbitrarily defined this parameter with a maximum length of 192 bytes as that is the largest number of displayable characters available with a single-byte EBCDIC CCSID. You can certainly increase or decrease this size if you so desire.

The second parameter, Base, is the character variable that is to be tested for any characters that are not found in the Comparator value; it maps to the second parameter of the %Check built-in. As with Comparator, the declared length of 1024 is totally arbitrary. You can increase or decrease this size to whatever length is appropriate for your use of the command.

The third parameter, Pos, is a return variable and will identify the first character found in Base that does not exist in Comparator. As with the %Check built-in, if all characters of Base are found in Comparator, then Pos will be set to 0. Otherwise, Pos will identify the first position of Base not in Comparator.

The %Check built-in does provide one parameter that is not being implemented today—namely, a starting position within Base. The %Check built-in defaults to a starting position of 1, which is where our CHKCHR command as currently written will always start, but you can specify other values with the RPG built-in. The ability to specify a user-selected starting position with the CHKCHR command is not difficult to do and can be demonstrated in a future article if there is interest in such a capability. For now, I'm ignoring this starting position capability in order to keep the discussion of the CPP straightforward.

Assuming that the previous command source is stored in member CHKCHR of source file QCMDSRC, you can create CHKCHR with the following command:

CrtCmd Cmd(ChkChr) Pgm(ChkChrCPP) Allow(*BPgm *IPgm *BMod *IMod)

This CRTCMD indicates that the command processing program (CPP) of CHKCHR is program CHKCHRCPP and that the command can be run from within a program or a module. Due to Pos being a return value, you will not be able to run the CHKCHR command interactively from a command line.

The CHKCHRCPP Command Processing Program

Below is the source for our CPP, ChkChrCPP.

Pgm       Parm(&Comparator &Base &Pos)                          

Dcl       Var(&Comparator) Type(*Char) Len(192)                

Dcl       Var(&Base)       Type(*Char) Len(1024)                

Dcl        Var(&Pos)       Type(*UInt)                          


Dcl       Var(&Null_Comp) Type(*Char) Len(193)                

Dcl       Var(&Null_Base) Type(*Char) Len(1025)                

Dcl       Var(&Null_Char) Type(*Char) Len(1)   Value(x'00')  

Dcl       Var(&Len)       Type(*UInt)                          


ChgVar     Var(&Null_Comp) Value(&Comparator *TCat +            


ChgVar     Var(&Null_Base) Value(&Base *TCat &Null_Char)        

CallPrc   Prc('strspn') Parm((&Null_Base) (&Null_Comp)) +      



CallPrc   Prc('strlen') Parm((&Null_Base)) RtnVal(&Len)    

If         Cond(&Pos = &Len) Then( +                        

             ChgVar Var(&Pos) Value(0))                    

Else       Cmd(CHgVar Var(&Pos) Value(&Pos + 1))            



Assuming that the preceding source is stored in member CHKCHRCPP of source file QCLSRC, you can, if your system is V6R1 or higher, use the following command to create the CHKCHRCPP program:

CrtBndCL Pgm(ChkChrCPP)

If you system is V5R4 or earlier, you will need to use the two following commands to create the CHKCHRCPP program:

CrtCLMod Module(ChkChrCPP)  

CrtPgm Pgm(ChkChrCPP) BndDir(QC2LE)

If you're not interested in how CHKCHRCPP works, you can now jump to the section titled "Testing the CHKCHR Command." Otherwise, just continue reading.

The CHKCHRCPP program declares three parameters (&Comparator, &Base, and &Pos), which respectively define the three keywords of the CHKCHR command. The program also declares four internal variables (&Null_Comp, &Null_Base, &Null_Char, and &Len), which are used in conjunction with two system APIs that are called as part of CHKCHRCPP processing.

These APIs, which are standard in the i operating system and considered part of the C language run-time, are strspn and strlen. The strspn API returns the initial length (or span) of the Base variable string, which consists only of those characters that are found in Comparator. The API is documented here and, like most C run-time APIs, does not provide for an explicit parameter indicating the length of the Base variable. Instead, the API calculates the length of the parameter based on finding a null byte (a byte with a value of x'00') within the variable. The null byte then indicates the end of the character variable. To insert this null byte into the Base and Comparator values, the program sets the variables &Null_Comp and &Null_Base to the values of &Comparator and &Base, respectively, with the variable &Null_Char being concatenated with blank truncation. The key item about &Null_Comp and &Null_Base is that they are declared as being 1 byte larger than the corresponding non-null terminated variables &Comparator and &Base. This additional byte in their declared lengths is to ensure that there is always room for the ending null byte.

The strspn API is then called, passing these null-terminated values, and the API returns &Pos. &Pos, as mentioned earlier, represents the number of character positions spanned within the base argument where the character is found within the comparator argument. So a &Pos value of 0 indicates that the very first character of the base string was not in the comparator, a value of 3 that the first three characters of the base string were in the comparator but the 4th character was not (assuming the base argument is greater than 3 bytes in length), and a value of &Pos that is equal to the length of the base argument means that all characters in the base string were found in the comparator. These returned position values are not at all the same as the values returned by the RPG built-in %Check, but we'll be making the necessary adjustments shortly.

To determine whether or not invalid characters were encountered by the strspn API, we now need to determine the length of the base argument. To do this, the strlen API is called. The strlen API, documented here, returns the length of a character string, excluding the ending null byte (&Null_Char). The strlen API is called, passing the null-terminated variable &Null_Base, and the API returns &Len. &Len is now the length of the base argument, not including the null byte terminator.

CHKCHRCPP then compares the values of &Pos and &Len. If they are equal, then no "invalid" characters were encountered and the program returns the value of 0, compatible with the %Check built-in. If &Pos and &Len are not equal, then the program adds 1 to &Pos and returns this value. As &Pos prior to the addition represents the number of initial characters in &Base that were "valid," adding 1 identifies the first character that was not "valid."

Testing the CHKCHR Command

As the CHKCHR command cannot be run from the command line (due to the return parameter &Pos), we will write a simple CL program (UseChkChr) that can be called from the command line. The program will accept two parameters—the comparator string and the base string—and display a message related to the results of the CHKCHR command that is run. This is the source for USECHKCHR:

Pgm       Parm(&Comp_In &Base_In)                                  

Dcl       Var(&Comp_In) Type(*Char) Len(32)                        

Dcl        Var(&Base_In) Type(*Char) Len(32)                        


Dcl       Var(&Pos)     Type(*UInt)                                

Dcl       Var(&Pos_Char) Type(*Char) Len(5)                          


ChkChr     Comparator(&Comp_In) Base(&Base_In) Pos(&Pos)            

If         Cond(&Pos *NE 0) Then(Do)                                

             ChgVar    Var(&Pos_Char) Value(&Pos)                  

             SndPgmMsg Msg('Invalid character ' *Cat +  

                           %sst(&Base_In &Pos 1) *Cat +

                           ' at ' *Cat +                

                            &Pos_Char) +                



Else       Cmd(SndPgmMsg Msg('Everything is OK') +                  



Assuming that the preceding source is stored in member USECHKCHR of source file QCLSRC, you can use the following command to create the USECHKCHR program:

CrtBndCL Pgm(UseChkChr)

From the command line, we can now test a few scenarios.

Entering the command Call UseChkChr ('abc' 'cabbage') will result in the message "Invalid character g at 00006" as the sixth character of 'cabbage' (the "g") is not in the list of valid values ('abc').

Entering the command Call UseChkChr ('abc' 'a cabbage') will result in the message "Invalid character  at 00002" as the second character of 'a cabbage' (the blank space) is not in the list of valid values ('abc').

Entering the command Call UseChkChr (' abc' 'a cabbage') will result in the message "Invalid character g at 00008" as the eighth character of 'a cabbage' (the "g") is not in the list of valid values (' abc') where a blank precedes the 'a'. Note that, due to the use of *TCAT when concatenating &Null_Char to &Null_Comp, we do not want the blank character to be the last character of valid characters (as it will be removed during the concatenation operation).

Entering the command Call UseChkChr (' abcge' 'a cabbage') will result in the message "Everything is OK" as all characters in the string 'a cabbage' are now found in the comparator string.

Entering the command Call UseChkChr ('0123456789' '1,234.50') will result in the message "Invalid character , at 00002" as the second character of '1,234.50' (the comma) is not in the list of valid values ('0123456789').

More CL Questions?

Wondering how to accomplish a function in CL? Send your CL-related questions to me at This email address is being protected from spambots. You need JavaScript enabled to view it..


Bruce Vining

Bruce Vining is president and co-founder of Bruce Vining Services, LLC, a firm providing contract programming and consulting services to the System i community. He began his career in 1979 as an IBM Systems Engineer in St. Louis, Missouri, and then transferred to Rochester, Minnesota, in 1985, where he continues to reside. From 1992 until leaving IBM in 2007, Bruce was a member of the System Design Control Group responsible for OS/400 and i5/OS areas such as System APIs, Globalization, and Software Serviceability. He is also the designer of Control Language for Files (CLF).A frequent speaker and writer, Bruce can be reached at This email address is being protected from spambots. You need JavaScript enabled to view it.. 

MC Press books written by Bruce Vining available now on the MC Press Bookstore.

IBM System i APIs at Work IBM System i APIs at Work
Leverage the power of APIs with this definitive resource.
List Price $89.95

Now On Sale



Support MC Press Online

$0.00 Raised:

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: