02
Sat, Nov
2 New Articles

What's New in CL Programming

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

In the last two releases, IBM has made huge and unprecedented changes in the CL programming language. Until V5R3 and V5R4, CL seemed about as dormant as DOS! But now, we can all thank IBM for beefing up this language and providing us with many great features. These enhancements improve CL's ability to interoperate with other languages and generally make CL easier and more productive to work with.

These are some of the enhancements:

  • Support for integer and unsigned integer data types
  • Support for pointer data types
  • Select
  • DoUntil loop
  • DoWhile loop
  • DoFor loop
  • Subroutines
  • Support for processing up to five files

Integers and Unsigned Integers

These new data types provide an efficient method of storing certain numeric data. Obviously, if the data contains decimal values, then these two data types are inappropriate, but otherwise, they provide an even more compact form than the traditional packed (*DEC) format. The following chart illustrates the maximum values expressed by the *INT, *UINT, and *DEC formats (for comparison).

Relative Data Type Size and Maximum Values

Data Type
Size in Bytes
Maximum Value
Integer
*INT
2
32,000
Unsigned Integer
*UINT
2
64,000
Packed
*DEC
2
999
Integer
*INT
4
2 Billion
Unsigned Integer
*UINT
4
4 Billion
Packed
*DEC
4
99,999


The code statements below show the definition of the new integer data types with the traditional DCL statement.

DCL         &INT1         *INT   2   VALUE(-1000) 
DCL         &INT2         *INT   4   VALUE(-100000)
DCL         &INT3         *UINT  2   VALUE(1000)
DCL         &INT4         *UINT  4   VALUE(100000)

A simple example of when to use one of these is any time that you need a "counter" or "index" variable. In the example shown below in Figure 1, the program receives a file name as a parameter; it attempts to create an achieved copy of the object and then deletes the original. If it fails to get a lock, it retries up to three times before giving up.

PGM  PARM(&FILE &LIB)                            
     DCL &FILE *CHAR (10)                          
     DCL &LIB  *CHAR (10)                         
     DCL &X    *UINT (2)                        
                              
      CRTDUPOBJ  OBJ(&FILE) FROMLIB(&LIB) OBJTYPE(*FILE) +   
                 TOLIB(ARCHIVE) DATA(*YES)             
      MONMSG MSGID(CPF0000) EXEC(DO)                  
             DLTF ARCHIVE/&FILE              
             CRTDUPOBJ  OBJ(&FILE) FROMLIB(&LIB) OBJTYPE(*FILE) + 
             TOLIB(ARCHIVE) DATA(*YES)                     
      ENDDO                                                
                               
DLT:  DLTF &LIB/&FILE                                            
      MONMSG MSGID(CPF0000) EXEC(DO)                             
             CHGVAR &X (&X +1)                                    
             IF COND(&X >= 3) THEN(DO)                         
                 SNDUSRMSG  MSG('File' *BCAT &FILE *BCAT 'in library' +
                          *BCAT &LIB *BCAT 'Was not destroyed.') +    
                          MSGTYPE(*INFO) TOUSR(*REQUESTER)           
                 GOTO END                                     
             ENDDO                                        
             GOTO DLT                                     
      ENDDO                                             
                                                           
      SNDUSRMSG  MSG('File' *BCAT &FILE *BCAT 'in library ' + 
                 *BCAT &LIB *BCAT 'destroyed.') +            
                 MSGTYPE(*INFO) TOUSR(*REQUESTER)           
                                 
END:         ENDPGM                         

Figure 1: Archive and delete the selected file.

Pointers

Pointers are fairly new for most RPG programmers, but they have been around for a long time and are commonly used in other languages, such as C. Unlike most other data types, pointers do not contain data. Instead, they contain the memory address for a piece of data. Each pointer is 16 bytes long, which corresponds to the size of a memory address.

These sophisticated variables might seem out of place in CL programs, which are typically fairly simple programs. However, for greater interoperability with other languages and APIs, pointers provide much-needed flexibility.

The example shown in Figure 2 illustrates how to use pointers to scan through a large text field holding the current user portion of the library list and searching for a specific library.

PGM                                                                 
     DCL &LIBL *CHAR (2750)                                         
     DCL &PTR *PTR ADDRESS(&LIBL)                                   
     DCL &LIB *CHAR (10) STG(*BASED) BASPTR(&PTR)                   
                                                                    
             RTVJOBA    USRLIBL(&LIBL)                              
                                                                    
CHECK:       IF COND(&LIB = 'EUREKA') THEN(DO)          
             SNDUSRMSG  MSG('I Found it!') +                        
                        MSGTYPE(*INFO) TOUSR(*REQUESTER)            
                        GOTO END                                    
             ENDDO                                                  
                                                                    
             CHGVAR VAR(%OFS(&PTR)) VALUE(%OFS(&PTR) + 11)          
             GOTO CHECK                                             
END:         ENDPGM                    

Figure 2: Search the library list.

The field &PTR is defined as a *PTR data type and is initialized with the address of &LIBL. The variable &LIB is "based" on &PTR. This means that it does not have its own location in storage (memory). Rather, it moves to the location given in &PTR. When this program loads, &LIB initially overlays the first 10 bytes of &LIBL. Each time the program loops, the pointer moves 11 characters farther down the LIBL field. The offset function (%OFS) allows the CL program to easily adjust the location of the pointer by simply adding or subtracting a number of bytes. The program uses an increment of 11 because there is one byte of blank data between each library name in the list, and each library name is 10 characters long, so the total is 11.

SELECT Statement

The SELECT statement is nothing more than a specialized "IF" structure. It defines a set of mutually exclusive tests. Each test has a corresponding command to execute upon a successful test. As soon as that command is complete, control passes to the statement after the ENDSELECT.

So while this does not present any truly new ability, it does provide a much easier to read and understand logical control for CL programs. Figure 3 illustrates the use of the select statement by sending users to a different menu based upon their user class.

PGM                                                       
     DCL &CLASS *CHAR (10)                                
                                                          
             RTVUSRPRF  USRCLS(&CLASS)                    
                                                          
             SELECT                                       
             WHEN COND(&CLASS = '*SECOFR') THEN( +        
                  GOTO UBERLEET)                         
             WHEN COND(&CLASS = '*SECADM') THEN( +        
                  GOTO NOTSOUBER)                         
             WHEN COND(&CLASS = '*SYSOPR') THEN( +        
                  GOTO REALWORK)                          
             WHEN COND(&CLASS = '*PGMR') THEN( +          
                  GOTO GEEKDOM)                           
             OTHERWISE CMD(GOTO LOCKDOWN)                          
             ENDSELECT  
END:         ENDPGM     

Figure 3 : Send the user to the correct menu.

DOUNTIL Statement

The DOUNTIL command provides a nice, easy-to-use tool for looping until some event happens. The example in Figure 4 shows how to use DOUNTIL to force a program to loop continuously until the job, subsystem, or the system itself is shut down.

PGM                                          
     DCL &ENDJOB *LGL               
                                                         
             DOUNTIL COND(&ENDJOB)           
                  CALL PGM1                  
                  CALL PGM2                  
                  CALL PGM3 
RTVJOBA    ENDSTS(&ENDJOB)                       
             ENDDO                           
                                             
END:         ENDPGM                            

Figure 4: Loop through Pgm1, Pgm2, and Pgm3 until EOJ.

It's important to note that DOUNTIL does not test the value of &ENDJOB until after the code inside the loop has processed once. The test is performed when control of the program reaches the ENDDO for the loop.

DOWHILE Statement

If DOUNTIL's test at the end of the loop bugs you, you could use DOWHILE instead. It is similar in function. However, it loops while the condition is true and exits the loop when it is not true. And the test is performed at the beginning of the loop, so there is no guarantee that the code inside the loop will ever execute.

PGM                                          
     DCL &ENDJOB *LGL                        
                                             
             RTVJOBA    ENDSTS(&ENDJOB)      
                                             
             DOWHILE COND(*NOT &ENDJOB)           
                  CALL PGM1                  
                  CALL PGM2                  
                  CALL PGM3 
             RTVJOBA    ENDSTS(&ENDJOB                 
             ENDDO                           
                                             
END:         ENDPGM                            

Figure 5: Loop through Pgm1, Pgm2, and Pgm3 until EOJ.

DOFOR Statement

If you have a loop that executes a specific number of times, DOFOR will come in handy. Figure 6 illustrates a modified version of the program shown in Figure 1. This version uses DOFOR to control how many times the delete retries.

PGM  PARM(&FILE &LIB)      
     DCL &FILE *CHAR (10)            
     DCL &LIB  *CHAR (10)     
     DCL &X    *INT  (2)                 
     DCL &ERR  *LGL 

      CRTDUPOBJ  OBJ(&FILE) FROMLIB(&LIB) OBJTYPE(*FILE) +       
                 TOLIB(ARCHIVE) DATA(*YES)                     
      MONMSG MSGID(CPF0000) EXEC(DO)                             
             DLTF ARCHIVE/&FILE                                   
             CRTDUPOBJ  OBJ(&FILE) FROMLIB(&LIB) OBJTYPE(*FILE) + 
             TOLIB(ARCHIVE) DATA(*YES)                     
      ENDDO                                                
            CHGVAR VAR(&ERR) VALUE(‘1’)          
      DOFOR VAR(&X) FROM(1) TO(3) BY(1)
            DLTF &LIB/&FILE         
            MONMSG MSGID(CPF0000) EXEC(ITERATE)
CHGVAR VAR(&ERR) VALUE(‘0’)                             
            LEAVE                       
      ENDDO                                        
      IF COND(&ERR) THEN( +                         
                 SNDUSRMSG  MSG('File' *BCAT &FILE *BCAT 'in library' +
                          *BCAT &LIB *BCAT 'Was not destroyed.') +    
                          MSGTYPE(*INFO) TOUSR(*REQUESTER))
ELSE CMD( +
      SNDUSRMSG  MSG('File' *BCAT &FILE *BCAT 'in library ' + 
                 *BCAT &LIB *BCAT 'destroyed.') +            
                 MSGTYPE(*INFO) TOUSR(*REQUESTER))          
                         
END:         ENDPGM              

Figure 6: Archive and delete the selected file.

Subroutines

OK, is it just me, or is it incredible that IBM has added subroutine support to CL programs? While it's true that a lot of CL programs are far too simple to benefit from subroutines, they're still very useful in plenty of places. Figure 7 shows the code for a program that calls several programs and monitors for errors in each one. A subroutine provides common error-handling for the program.

PGM                                                         
     DCL &MSG *CHAR (80)
     DCL &ANS *CHAR (1)                     
     DCL &X   *INT  (2)                                     
                                                            
     DOUNTIL (&X = 1)                                       
             CHGVAR &X 1                                    
             CALL PGM1                                      
             MONMSG MSGID(CPF0000) EXEC(CALLSUBR ERR)       
     ENDDO                                                  
                                                            
     DOUNTIL (&X = 2)                                       
             CHGVAR &X 2                                    
             CALL PGM2                                      
             MONMSG MSGID(CPF0000) EXEC(CALLSUBR ERR)       
     ENDDO                                                  
     DOUNTIL (&X = 3)                                  
             CHGVAR &X 3                       
             CALL PGM3                            
             MONMSG MSGID(CPF0000) EXEC(CALLSUBR ERR)    
     ENDDO                                   
                       
  SUBR     SUBR(ERR)             
                        
             RCVMSG     MSG(&MSG) 
             SNDUSRMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&MSG) + 
                          VALUES(C I D R) DFT(R) TOMSGQ(*SYSOPR) + 
                          MSGRPY(&ANS)     
                         
             SELECT       
             WHEN (&ANS = C) THEN(+        
             SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&MSG) +
                          MSGTYPE(*ESCAPE))          
             WHEN (&ANS = R) THEN(+                                
             CHGVAR &X 0)                                          
             WHEN (&ANS = D) THEN(DO)                              
             DUMPCLPGM                                             
             SNDPGMMSG  MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&MSG) +
                          MSGTYPE(*ESCAPE))                        
             ENDDO                                                 
             ENDSELECT                                             
                                                                   
  ENDSUBR                                                          
END:         ENDPGM                                                

Figure 7: Provide error-handling.

Notice that the calls to PGM1, PGM2, and PGM3 are all wrapped up in a Do loop. That loop allows the retry function of the error-handling subroutine to work. If an error occurs and the user takes an "R" to retry, &X is set to 0 and the loop will continue calling the program again. If no error occurs or if a different option is taken, &X will be unchanged, ending the loop.

Multiple Files

Another great addition to CL is the ability to handle more than one file in a single program. Sure, the CL language is not intended for intensive I/O-handling programs; however, sometimes it's convenient to read two or more files in the same program. Figure 8 shows a CL program that searches a list of libraries for source physical files and then performs a FNDSTRPDM command against each of these files, looking for a specified string.

PGM          PARM(&TEXT)                                           
             DCLF       FILE(APPLIBS) OPNID(LIBS)                  
             DCLF       FILE(QAFDBASI) OPNID(FILES)                
             DCL        VAR(&TEXT) TYPE(*CHAR) LEN(40)             
             
     /* LOOP THROUGH EACH LIBRARY IN THE APPLIBS FILE */    
     /* AND ADD ALL OF FILES IN EACH TO QGPL/FILES    */          
             DOWHILE    COND(1 = 1)                                
             RCVF       OPNID(LIBS)                                
             MONMSG MSGID(CPF0864) EXEC(LEAVE)                     
                                                                   
             DSPFD      FILE(&LIBS_LIBNAME/*ALL) TYPE(*BASATR) +   
                          OUTPUT(*OUTFILE) OUTFILE(QTEMP/FILES) +  
                          OUTMBR(*FIRST *ADD)                      
             ENDDO                                                 

     /* READ EACH FILE IN QGPL/FILES AND PERFORM A FNDSTRPDM */    
     /* AGAINST IT.  SEARCHING FOR THE PARM &TEXT            */     
             OVRDBF     FILE(QAFDBASI) TOFILE(QTEMP/FILES)         
             DOWHILE    COND(1 = 1)                                
             RCVF       OPNID(FILES)          
             MONMSG MSGID(CPF0864) EXEC(LEAVE)       
             IF (&FILES_ATDTAT = 'S' *AND +       
                 &FILES_ATFTYP = 'P') THEN(DO)      
             FNDSTRPDM  STRING(&TEXT) +      
                          FILE(&FILES_ATLIB/&FILES_ATFILE) + 
                          MBR(*ALL) OPTION(*NONE) PRTMBRLIST(*YES) +
                          PRTRCDS(*NONE)    
             MONMSG     MSGID(PDM0000)                
             ENDDO      
             ENDDO      
             DLTOVR     FILE(QAFDBASI)           
                                
ENDPGM                                                               

Figure 8: Read data from multiple files.

Each of the declare files have an OPNID parameter (LIBS and FILES, respectively) that provides a unique identifier for the files. The RCV command should reference the appropriate file ID to ensure reading the next record from the appropriate file. Each of the fields from the files must also use the open ID as a prefix to the field name, such as &FILES_ATFILE. The field name is ATFILE, and the open ID is FILES, so the variable name in the CL program is &FILES_ATFILE. If you are using only one file in the CL program, you can omit the open ID from the RCV command and the field names.

Pleasant Surprises

While it is doubtful that many shops will jump to V5R4 just to get these new features in CL, it's still true that most shops will be happy to take advantage of these enhancements as they move up to the current release. The enhancements to CL in the last two releases also leave us wondering what pleasant surprises might be in store for the next release.

Kevin Forsythe is the author of the new book SQL for eServer i5 and iSeries. He has over 18 years of experience working with the iSeries platform and its predecessors. He has been a member of the DMC team for the past nine years. Kevin's primary responsibility is providing iSeries education, but he also provides customers with project management, system design, analysis, and technical construction. In addition to his technical skills (RPG IV, CL, OS/400, SQL, FTP, Query, VB, Net.Data), Kevin possesses the ability to communicate new and complex concepts to his students. He has been the primary instructor for DMC's iSeries-based AS/Credentials training courses since 1997 and has authored courses such as Advanced ILE, SQL, Embedded SQL, Operations Navigator, and Intro to WebSphere Studio. An award-winning speaker, he has spoken at every COMMON Conference since the spring of 2000.

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

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: