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:
The TRMLFTCHR UIM Source
: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.
:p.:hp2.Restrictions::ehp2.
:ul.
:li.
The TRMLFTCHR command is valid only in CL programs.
:eul.
:ehelp.
.*******************************************
.* 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.
:parml.
:pt.:pv.character-value:epv.
:pd.
Specify the name of the character variable.
:eparml.
:ehelp.
.*******************************************
.* 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.
:parml.
:pt.:pk def.0:epk.
:pd.
Remove zero (0) values starting from the left of the VAR parameter
value.
:pt.:pv.character-value:epv.
:pd.
Specify the characters to be trimmed from the left of the VAR
parameter.
:eparml.
:ehelp.
.*******************************************
.* 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.
:parml.
:pt.:pk def.*TRMCHR:epk.
:pd.
The first specified TRMCHR parameter value is to be used when all
characters of the VAR parameter are trimmed.
:pt.:pv.character-value:epv.
:pd.
Specify the character value to be returned when all characters of the
VAR parameter are trimmed.
:eparml.
:ehelp.
.**************************************************
.*
.* Examples for TRMLFTCHR
.*
.**************************************************
:help name='TRMLFTCHR/COMMAND/EXAMPLES'.
Examples for TRMLFTCHR - Help
:xh3.Examples for TRMLFTCHR
:p.:hp2.Example 1: Simple Command Example:ehp2.
:xmp.
TRMLFTCHR VAR(&CHAR10)
:exmp.
:p.This command will trim all leading zero (0) values from the
CL variable &CHAR10 and return the remaining characters left
adjusted.
.*
:p.:hp2.Example 2: More Complex Command Example:ehp2.
:xmp.
TRMLFTCHR VAR(&CHAR10) TRMCHR(* ' ')
ALLTRMCHR(X)
:exmp.
: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.
:ehelp.
.**************************************************
.*
.* 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.
:DT.TRM0009
:DD.&MSG(TRM0009).
:EDL.
:ehelp.
:epnlgrp.
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:
CRTPNLGRP PNLGRP(TRMLFTCHR)
The TRMLFTCHR CPP Source
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)
EndDo
Else Cmd(Do)
ChgVar Var(%ofs(&TrmChrPtr)) +
Value(%ofs(&TrmChrPtr) + 1)
Iterate
EndDo
EndDo
Leave
EndDo
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)
EndDo
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)
EndDo
EndDo
EndDo
Else Cmd(Do)
If Cond(&All_TrmChr *EQ &XFF) Then(Do)
ChgVar Var(&TrmChrPtr) Value(%addr(&First_Trm))
ChgVar Var(&Char_Tgt) Value(&TrmChr)
EndDo
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)
EndDo
EndDo
Return
Error:
SndPgmMsg MsgID(TRM0009) MsgF(Vining/UserMsgf) +
MsgType(*Escape)
EndPgm
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:
CRTBNDCL PGM(TRMLFTCHR)
or
CRTCLPGM PGM(TRMLFTCHR)
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')) +
Prompt(TRM0004)
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:
CRTCMD CMD(TRMLFTCHR) PGM(TRMLFTCHR) ALLOW(*IPGM *BPGM *IMOD *BMOD) +
PMTFILE(USERMSGF)HLPPNLGRP(TRMLFTCHR) HLPID(*CMD)
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:
GENCMDDOC CMD(TRMLFTCHR) TODIR('/QSYS.LIB/VINING.LIB/QPNLSRC.FILE')
GENOPT(*UIM)
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:
GENCMDDOC CMD(TRMLFTCHR) TODIR(/) TOSTMF(TrmLftChr.html) GENOPT(*HTML)
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">
<html>
<head><META http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Trim Left Characters (TRMLFTCHR)</title>
</head>
<body bgcolor="white">
<a></a>
<h2>Trim Left Characters (TRMLFTCHR)</h2>
<table width="100%">
<tr>
<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>
<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>
</tr>
</table>
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 PowerCL.com. 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
as/400, os/400, iseries, system i, i5/os, ibm i, power systems, 6.1, 7.1, V7,
LATEST COMMENTS
MC Press Online