Converting CASE in CL
November 14, 2012 Bob Cozzi
When I write CL, I long for some of the ease-of-use features IBM added over the last 10 years to RPG IV. Simple things like %XLATE or %SCAN or %EDITC would be nice.
For example, a client of mine had a CL program that required several parameters, one of which was a user ID. However, the non-IBM i platform on which that information was entered did not automatically convert lowercase letters to all uppercase. When the program would try to verify the user profile, it would almost always fail.
Initially my client wanted to use one of the legacy APIs such as QDCXLATE or QTBXLATE. But these APIs were created for tape conversions and were never intended to be used for data conversion on an on-going basis. Their tables are not maintained, and may not always produce the results one would expect.
RPG IV has its %XLATE built-in function, which is a poor man’s ToLower or ToUpper function. But there is also an IBM i API that uses CCSID-safe conversion routines to perform case conversions. That API is QlgConvertCase.
I’ve been using this API for years, but this was the first time I had needed to leverage it in CL. I had previously created an RPG IV prototype for this API. I reproduce it here so that we have a common foundation for this API, rather than using the QSYSINC version of the prototype, which is in C.
D QlgCvtCase PR extProc('QlgConvertCase') D ctrlBlock Const LikeDS(FRCB_T) D inString 65535A Const Options(*VARSIZE) D OutString 65535A Options(*VARSIZE) D nLength 10I 0 Const D apiError LikeDS(QUSEC_T) OPTIONS(*VARSIZE)
The parameters are pretty standard except for the first one–the so called Control Block. This is a data structure named FRCB. I’ve created a template for it named FRCB_T in my own tool kit. I reproduce it here for completeness:
D FRCB_T DS QUALIFIED D reqType 10I 0 Inz(1) D CCSID 10I 0 Inz(0) // 0=ToUpper; 1=ToLower; D ToLower 10I 0 Inz(0) D Reserved 10A Inz(*ALLX'00')
The FRCB data structure controls how the QlgConvertCase API behaves. If the REQTYPE subfield equals 1, the API does a CCSID-save upper/lower case conversion. That conversion is based on the value of the TOLOWER subfield. If TOLOWER = 1, the data is converted to all lower case. If it equals 0, it is converted to all uppercase.
We need to convert the data structure to CL in order to call the API. In V5R4, CL gained limited data structure support and other features that make it easier to define data structures. To define the FRCB data structure in CL, first declare the data structure itself (&FRCB in our example). Each subfield needs to specify STG(*DEFINED) and DEFVAR(&FRCB xx), where xx is the position within &FRCB where the subfield is located.
Here’s how my version turned out:
FRCB: /* FRCB Structure in CL */ DCL VAR(&FRCB) TYPE(*CHAR) LEN(22) DCL VAR(&FRCB_REQ) TYPE(*INT) STG(*DEFINED) + LEN(4) DEFVAR(&FRCB) DCL VAR(&FRCB_CCSID) TYPE(*INT) STG(*DEFINED) + LEN(4) DEFVAR(&FRCB 5) DCL VAR(&FRCB_Lower) TYPE(*INT) STG(*DEFINED) + LEN(4) DEFVAR(&FRCB 9) DCL VAR(&FRCB_RES) TYPE(*CHAR) STG(*DEFINED) + LEN(10) DEFVAR(&FRCB 13)
Interestingly, when you use the STG(*DEFINED) feature, you cannot use the VALUE keyword on the subfields. Therefore, you cannot initialize data structure subfields in CL. Instead you have to use CHGVAR.
The QlgConvertCase API comes in two versions, a subprocedure and a program. The program object is the form I’ll use in CL since CL plays nicer with program calls. The program version of the API is named QLGCNVCS. It has identical parameters to its subprocedure counterpart; therefore the FRCB data structure is going to be put to good use.
As with all good APIs, the numeric parameters are 4 byte integers. These equate to TYPE(*INT) LEN(4) in CL and 10i0 in RPG IV. For the most part, the entire data structure should be initialized to all X’00’. Since CL does not offer any kind of function to do that, we are forced to hard code it on the VALUE keyword as shown in the example below.
Two subfields in the FRCB data structure must be initialized to values other than X’00’. The REQ subfield is always set to 1. This means a CCSID lower- to uppercase or upper- to lowercase conversion is being performed. That’s what we’re doing. The LOWER subfield is set to 1 to indicate lowercase conversion or 0 to indicate uppercase conversion. In my example, I convert to uppercase so it is set to 0.
Here’s the full example:
CVTCASE: PGM /* Example Convert Case in CL by Bob Cozzi */ DCL VAR(&FRCB) TYPE(*CHAR) LEN(22) DCL VAR(&FRCB_REQ) TYPE(*INT) STG(*DEFINED) + LEN(4) DEFVAR(&FRCB) DCL VAR(&FRCB_CCSID) TYPE(*INT) STG(*DEFINED) + LEN(4) DEFVAR(&FRCB 5) DCL VAR(&FRCB_Lower) TYPE(*INT) STG(*DEFINED) + LEN(4) DEFVAR(&FRCB 9) DCL VAR(&FRCB_RES) TYPE(*CHAR) STG(*DEFINED) + LEN(10) DEFVAR(&FRCB 13) DCL VAR(&NAME) TYPE(*CHAR) LEN(50) + VALUE('Cozzi test for itJungle.com article') DCL VAR(&OUTPUT) TYPE(*CHAR) LEN(50) DCL VAR(&LEN) TYPE(*INT) LEN(4) VALUE(50) DCL VAR(&QUSEC) TYPE(*CHAR) LEN(16) + VALUE(X'0000000000000000') MONMSG MSGID(CPF0000) CHGVAR VAR(&FRCB_REQ) VALUE(1) CHGVAR VAR(&FRCB_CCSID) VALUE(0) CHGVAR VAR(&FRCB_LOWER) VALUE(0) CHGVAR VAR(&FRCB_RES) VALUE(X'00000000000000000000') CONVERT: CALL PGM(QLGCNVCS) PARM(&FRCB &NAME &OUTPUT &LEN + &QUSEC) SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA('Name =' + *BCAT &NAME) MSGTYPE(*COMP) SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA('Upper=' + *BCAT &OUTPUT) MSGTYPE(*COMP) ENDPGM: ENDPGM
This CL program converts the text in the CL variable &NAME to all uppercase and stores the converted text in the &OUTPUT variable. The program then writes this information to the job log for you to review.
I originally tried to retrieve the job’s CCSID and use it, but since my system still has 65535 for its CCSID instead of 37 or 0, it didn’t work with this API. Using 0 means it will take the job’s CCSID anyway, so why bother trying to be explicit?
One thing I’ve learned from my years of writing programmer tools is that if you put in a little effort up front, you end up with a fairly simple, yet powerful tool that can be easily reused. There’s an old saying “The cheap person pays twice.” If I modify that a bit for our situation, I could say “the lazy programmer codes twice.”
Bob Cozzi is author of The Modern RPG IV Language book, and has been working with RPG since the 1970s. Bob’s latest project is COZTOOLS a library of features that add to RPG IV’s built-in functions. Send your questions or comments for Bob to Ted Holt via the IT Jungle Contact page.