The CL Corner: Getting Your Commands Out to the World

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

Learn about some additional capabilities related to user commands.


In the last article, "Need Some Help with That Command? Part II," we concluded our introduction to the User Interface Manager (UIM) as it relates to providing command help text for the command TRMLFTCHR. The complete source for the TRMLFTCHR panel group, which has only been published in pieces in past articles, is this:


:pnlgrp submsgf='VINING/USERMSGF'.                                    


.*  Help for command TRMLFTCHR                                        


:help name='TRMLFTCHR'.                                               

&msg(TRM0001). - Help                                                  

:p.The &msg(TRM0001). (TRMLFTCHR) command changes the value of a CL   

character variable by trimming (removing) specified characters from the

left of the specified CL variable.  The result is returned left       

adjusted and blank padded.                                            




The TRMLFTCHR command is valid only in CL programs.                   




.*   Help for parameter VAR                                         


:help name='TRMLFTCHR/VAR'.                                          

&msg(TRM0002). (VAR) - Help                                         

:xh3.&msg(TRM0002). (VAR)                                           

:p.Specifies the CL character variable whose value is to be changed.

:p.This is a required parameter.                                     




Specify the name of the character variable.                         




.*   Help for parameter TRMCHR                                      


:help name='TRMLFTCHR/TRMCHR'.                                       

&msg(TRM0003). (TRMCHR) - Help                                        

:xh3.&msg(TRM0003). (TRMCHR)                                         

:p.Specifies one or more character values to be trimmed from the left

of the CL variable identified by the VAR parameter. Trimming of       

characters will end when a character value of the VAR parameter does 

not match any of the specified TRMCHR values.                        

:p.You can specify 50 values for this parameter.                     


:pt.:pk def.0:epk.                                                   


Remove zero (0) values starting from the left of the VAR parameter   




Specify the characters to be trimmed from the left of the VAR         





.*   Help for parameter ALLTRMCHR                                    


:help name='TRMLFTCHR/ALLTRMCHR'.                                    

&msg(TRM0004). (ALLTRMCHR) - Help                                    

:xh3.&msg(TRM0004). (ALLTRMCHR)                                      

:p.Specifies the left adjusted value to be returned in the VAR       

parameter when only characters specified by the TRMCHR parameter are 

found in the VAR parameter. This character value will be returned in 

the leftmost position of the VAR parameter with all following        

character positions set to blanks.                                   


:pt.:pk def.*TRMCHR:epk.                                             


The first specified TRMCHR parameter value is to be used when all    

characters of the VAR parameter are trimmed.                         



Specify the character value to be returned when all characters of the

VAR parameter are trimmed.                                           





.* Examples for TRMLFTCHR                                            



:help name='TRMLFTCHR/COMMAND/EXAMPLES'.                             

Examples for TRMLFTCHR - Help                                        

:xh3.Examples for TRMLFTCHR                                           

:p.:hp2.Example 1: Simple Command Example:ehp2.                      


TRMLFTCHR  VAR(&CHAR10)                                              


:p.This command will trim all leading zero (0) values from the       

CL variable &CHAR10 and return the remaining characters left         



:p.:hp2.Example 2: More Complex Command Example:ehp2.                


TRMLFTCHR   VAR(&CHAR10) TRMCHR(* ' ')                               



:p.This command will trim all leading blanks and asterisks from the  

CL variable &CHAR10 and return the remaining characters left adjusted.

If only blanks and asterisks are found in the &CHAR10 variable then  

the left adjusted value 'X' is to be returned.                       




.* Error messages for TRMLFTCHR                                 



:help name='TRMLFTCHR/ERROR/MESSAGES'.                          

&msg(CPX0005,QCPFMSG). TRMLFTCHR - Help                         

:xh3.&msg(CPX0005,QCPFMSG). TRMLFTCHR                           

:p.:hp3.*ESCAPE &msg(CPX0006,QCPFMSG).:ehp3.                    

:DL COMPACT.                                                    







Assuming that the preceding source is stored in member TRMLFTCHR of source file QPNLSRC, you can create the TRMLFTCHR panel group with the following command:




The previous column on this topic also introduced a change to the command processing program (CPP) for the TRMLFTCHR command. The change was to send the escape message TRM0009 in the case of an unexpected error. This is the complete source for the TRMLFTCHR CPP:


      Pgm        Parm(&Char_Parm &TrmChrParm &All_TrmChr)        

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

        Dcl        Var(&Char_Siz)   Type(*Int) Stg(*Defined) +   

                     DefVar(&Char_Parm 1)                        

        Dcl        Var(&First_Char) Type(*Char) Len(1) +         

                     Stg(*Defined)    DefVar(&Char_Parm 5)       


      Dcl        Var(&TrmChrParm) Type(*Char) Len(3)             

        Dcl        Var(&NbrTrmChr)  Type(*UInt) Len(2) +         

                     Stg(*Defined)    DefVar(&TrmChrParm 1)      

        Dcl        Var(&First_Trm)  Type(*Char) Len(1) +         

                     Stg(*Defined)    DefVar(&TrmChrParm 3)      


      Dcl        Var(&All_TrmChr) Type(*Char) Len(1)             


      Dcl        Var(&Char_Ptr)   Type(*Ptr)                     

      Dcl        Var(&Char)       Type(*Char) Len(1) +             

                   Stg(*Based)    BasPtr(&Char_Ptr)                


      Dcl        Var(&CharTgtPtr) Type(*Ptr)                       

      Dcl        Var(&Char_Tgt)   Type(*Char) Len(1) +             

                   Stg(*Based)    BasPtr(&CharTgtPtr)              


      Dcl        Var(&TrmChrPtr)  Type(*Ptr)                       

      Dcl        Var(&TrmChr)     Type(*Char) Len(1) +             

                   Stg(*Based)    BasPtr(&TrmChrPtr)               


      Dcl        Var(&Char_Pos)   Type(*UInt)                      

      Dcl        Var(&Char_Rem)   Type(*UInt)                      

      Dcl        Var(&Trm_Pos)    Type(*UInt)                      

      Dcl        Var(&XFF)        Type(*Char) Len(1) Value(x'FF')  


      MonMsg     MsgID(CPF0000 MCH0000) Exec(Goto CmdLbl(Error))   


      ChgVar     Var(&Char_Ptr) Value(%addr(&First_Char))           

      ChgVar     Var(&CharTgtPtr) Value(%addr(&First_Char))         


Trim: DoFor      Var(&Char_Pos) From(1) To(&Char_Siz)                

                 ChgVar Var(&TrmChrPtr) Value(%addr(&First_Trm))    

                 DoFor Var(&Trm_Pos) From(1) To(&NbrTrmChr)         

                       If Cond(&Char *EQ &TrmChr) Then(Do)          

                          ChgVar Var(%ofs(&Char_Ptr)) +             

                                   Value(%ofs(&Char_Ptr) + 1)       

                          Iterate CmdLbl(Trim)                      


                       Else Cmd(Do)                                 

                            ChgVar Var(%ofs(&TrmChrPtr)) +          

                                     Value(%ofs(&TrmChrPtr) + 1)    







      If         Cond(&Char_Pos *LE &Char_Siz) Then(Do)               

                 DoFor Var(&Char_Pos) From(&Char_Pos) To(&Char_Siz)    

                       ChgVar Var(&Char_Tgt) Value(&Char)             

                       ChgVar Var(%ofs(&CharTgtPtr)) +                

                                Value(%ofs(&CharTgtPtr) + 1)          

                       ChgVar Var(%ofs(&Char_Ptr)) +                  

                                Value(%ofs(&Char_Ptr) + 1)            



                 If    Cond(&Char_Ptr *NE &CharTgtPtr) Then(Do)       

                       ChgVar Var(&Char_Rem) Value( +                 

                                (%ofs(&Char_Ptr) - %ofs(&CharTgtPtr)))

                       DoFor Var(&Char_Pos) From(1) To(&Char_Rem)     

                             ChgVar Var(&Char_Tgt) Value(' ')       

                             ChgVar Var(%ofs(&CharTgtPtr)) +        

                                      Value(%ofs(&CharTgtPtr) + 1)  





      Else       Cmd(Do)                                            

                 If Cond(&All_TrmChr *EQ &XFF) Then(Do)             

                    ChgVar Var(&TrmChrPtr) Value(%addr(&First_Trm)) 

                    ChgVar Var(&Char_Tgt) Value(&TrmChr)            


                 Else Cmd( +                                        

                      ChgVar Var(&Char_Tgt) Value(&All_TrmChr))     

                 ChgVar Var(%ofs(&CharTgtPtr)) +                    

                          Value(%ofs(&CharTgtPtr) + 1)              

                 DoFor Var(&Char_Pos) From(2) To(&Char_Siz)         

                       ChgVar Var(&Char_Tgt) Value(' ')           

                       ChgVar Var(%ofs(&CharTgtPtr)) +           

                                Value(%ofs(&CharTgtPtr) + 1)     






      SndPgmMsg  MsgID(TRM0009) MsgF(Vining/UserMsgf) +          





Assuming that the preceding source is stored in member TRMLFTCHR of source file QCLSRC, you can create the TRMLFTCHR CPP with either of the following commands:







The TRMLFTCHR Command Source

And as long as we're providing complete source examples, here is the source for the TRMLFTCHR command:


Cmd        Prompt(TRM0001)                           

Parm       Kwd(Var) Type(*Char) Len(1) RtnVal(*Yes) +

             Min(1) Vary(*Yes *Int4) Prompt(TRM0002) 

Parm       Kwd(TrmChr) Type(*Char) Len(1) Dft(0) +   

             SpcVal((0)) Max(50) Prompt(TRM0003)     

Parm       Kwd(AllTrmChr) Type(*Char) Len(1) +       

             Dft(*TrmChr) SpcVal((*TRMCHR X'FF')) +  



Assuming that the preceding source is stored in member TRMLFTCHR of source file QCMDSRC, you can create the TRMLFTCHR command, linking it to the TRMLFTCHR help panel group and TRMLFTCHR CPP, with the following command:





At the conclusion of last month's article, it was also pointed out that while providing help text for the TRMLFTCHR command was reasonably easy, the help text wouldn't be of much aid if you weren't on an IBM i where you could prompt the command. The question posed then was "Wouldn't it be nice to also provide the help text on the Web?" This would enable just about anyone, anywhere, to access it at any time. Well, it turns out that putting the command help text on the Web (assuming you have a Web presence and are currently serving Web pages) is roughly one command away!

Generating TRMLFTCHR HTML Documentation

Recall that back in the article "Providing Help Text for a User Command," we used the Generate Command Documentation (GENCMDDOC) command to provide the initial UIM source for the TRMLFTCHR panel group. This is the command used at that time:





Having now created our help panel group, and associating our panel group with the TRMLFCHR command with the HLPPNLGRP and HLPID parameters of CRTCMD, we can now again use the GENCMDDOC command. But this time, we'll have an HTML document generated based on the contents of our panel group. To generate the file TrmLftChr.html in the root directory of the IFS, you could use the following command:




This will create a UTF-8 encoded HTML file whose source starts as shown below:


<!doctype html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">


<head><META http-equiv="Content-Type" content="text/html; charset=utf-8">

<title>Trim Left Characters  (TRMLFTCHR)</title>


<body bgcolor="white">


<h2>Trim Left Characters  (TRMLFTCHR)</h2>

<table width="100%">


<td valign="top" align="left"><b>Where allowed to run:  </b>

<ul><li>Batch program (*BPGM)</li>

<li>Interactive program (*IPGM)</li>

<li>Batch ILE CL module (*BMOD)</li>

<li>Interactive ILE CL module (*IMOD)</li>

</ul><b>Threadsafe:  </b>No


<td valign="top" align="right">

<a href="#TRMLFTCHR.PARAMETERS.TABLE">Parameters</a><br>

<a href="#TRMLFTCHR.COMMAND.EXAMPLES">Examples</a><br>

<a href="#TRMLFTCHR.ERROR.MESSAGES">Error messages</a></td>




What you have here is a "ready to go" HTML file that, if opened by a browser, will present the TRMLFTCHR command documentation in a style that is amazingly similar to the style used by sites such as the IBM Information Center and It just doesn't get much easier than that!

Supporting Multiple Language Environments

While we're on the topic of expanded access to command documentation, let's return to a topic that was mentioned back in the article "Providing Help Text for a User Command" under the heading "Do You Support Multiple Language Environments?" If you have reviewed the compiler listing from creating the panel group TRMLFTCHR, you may have noticed that many of the TRM messages that are referenced using the UIM &msg symbol result in the warning message CPD5BCB – A CCSID conversion error occured [sic] when message TRMxxxx was retrieved. This warning is due to message files defaulting to a CCSID of *HEX when created, which is really CCSID 65535, which essentially turns off CCSID conversions and results in the potential for incorrect message text in the panel group if variant characters exist in the first-level text of the referenced message description. That is, message text created by a U.S. job that contains the U.S. dollar sign ($) may, when used by a UK job running with a different CCSID, show the UK pound sign (£). As with the previously cited article, if all of your users run in the same national language. then you can skip to the next topic: Seeing the Forest Rather Than the Trees. But if you do have users running with different job CCSIDs, then continue reading.


Avoiding this potential error in data representation, and getting rid of the CPD5BCB warning message, is quite easy to do if you followed my earlier suggestion of using a non-65535 job CCSID when adding the various TRM* message descriptions. The Add Message Description (ADDMSGD) command actually records, by default, the job CCSID that is in effect when the message is added. In order to start using these stored message description level CCSIDs, rather than the message file CCSID of 65535, is as simple as using the Change Message File (CHGMSGF) command and specifying a CCSID of *MSGD, which equates to CCSID 65534. While many developers are familiar with CCSID 65535 and its behavior of turning off CCSID conversions, not as many have run into CCSID 65534. CCSID 65534 is a special CCSID that indicates to the system that the actual CCSID value to use can be found at the "next lower" level of the object. In the case of a message file, this "next lower" level is the specific message description.

Seeing the Forest Rather Than the Trees

When developing the TRMLFTCHR command, we started with the command definition, then developed the CPP, and ended by creating the help text for the command. I used this particular sequence in the hope that it would make it easier for most readers to understand how the various pieces work in providing a complete command (and of course to demonstrate some of the capabilities in command definition, CL, and UIM that are available to you). This sequence of development actions, however, is not what I would recommend when "really" developing a new command.


In my experience, I have found that creating the help text immediately after defining the command, rather than coding the CPP prior to the help text, is much more productive. Trying to express the function of the command, and the parameters, in a written form typically gets me thinking a lot more (and earlier) about the interplay of the parameters (and often the need for additional parameters and/or keywords) than if I simply start coding the CPP to my initial command design. That is, coding the CPP prior to the help text tends to focus me on the implementation of the discrete parameters (an individual tree or a specific cluster of trees), and I often don't realize the need for an additional parameter or keyword until I've gone quite a way down an implementation path—a path that then needs to be reworked (or more often rewritten) to accommodate my late discovery. Writing out the help text prior to writing the CPP helps me keep the big picture or "forest" in mind and often results in a much better design when I do start coding the CPP.


Writing out the help text prior to implementing the CPP also provides some additional benefits. One is that you can then distribute the help text out to others for their review and suggestions. Early critiquing of your design by way of sharing your written command help text documentation is generally much easier (and more productive) than by sharing a verbal description of your command. And certainly beats a code review as the first opportunity for others to comment on your code (though code reviews are certainly important, they're often done a bit late for you to easily accommodate new features in the command). A second benefit that I've seen is that help text written after the CPP has been coded tends to be more a guide to the user describing what you did rather than a guide to the user describing what they can do, which is what they really want from the help text.


As you code the CPP, you will of course encounter the need to occasionally update the command documentation. That's to be expected and, in my case, quite often involves the error message section of the command documentation. But I believe it's quite important to write out, early on, the command description, the parameter descriptions, and the examples section of the panel group.

The End

This concludes the saga of our TRMLFTCHR command. Over the course of several articles, we've taken advantage of many features available to you. Some of these features included command parameter lists, CL pointers, CL-based and defined storage, structured operators such as DOFOR, UIM help text, and HTML help text generation. Hopefully, besides being a learning opportunity for these features, TRMLFTCHR might even be a command that you can use within a CL program or two of your own.


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.. I'll try to answer your burning questions in future columns.

as/400, os/400, iseries, system i, i5/os, ibm i, power systems, 6.1, 7.1, V7,