Q: Is there an easy way to calculate the length of a string in CL, so I don't have to hard-code it? Can I calculate the length both with and without the trailing blanks?
A: There was actually a really good discussion about this subject in the System iNetwork forums this week. In this article, I include the answer that I thought was the best one, and I explain how the code works. I also provide a link to the original thread in the forums so you can read it yourself.
I felt the best answer in the thread came from the user named DéesseT. Some very short and simple CL programs can be used to create commands on your system for calculating the length of strings. Once the commands are compiled, you can use them over and over again, as if they were built in to the language.
The first one is called RTVDTALEN and calculates the length of the data—which is to say, the length of a variable after trailing blanks have been stripped:
Member RTVDTALEN, file QCMDSRC:
CMD 'Retrieve length of data'
PARM Var +
*X +
MIN(1) +
EXPR(*YES) +
VARY(*YES *INT2) +
PROMPT('CL variable name')
PARM DtaLen +
*INT2 +
RTNVAL(*YES) +
MIN(1) +
PROMPT('Variable for DTALEN (INT2)')
Member RTVDTALEN, file QCLSRC:
PGM (&Var &DtaLen)
DCL &Var *INT 2 /* CL variable name */
DCL &DtaLen *INT 2 /* Data length */
CHGVAR &DtaLen &Var
ENDPGM
This code works by taking advantage of the VARY(*YES) option specified for the first parameter of the command. Here's what it says about this parameter in the help text for the PARM command:
Varying length (VARY) - Help
Specifies whether the value that is passed to the command processing
program is preceded by a length value that indicates the number of
characters entered for the command parameter.
Note: The length value is the actual number of characters entered
for the command parameter, with trailing blanks removed. The length
value passed may be different than the defined parameter length or
the declared variable length. The length of the field containing
the character string data is determined by the defined length for
the parameter or the declared LEN for CL program variables. The
length value defines how many characters in the character string
data field were actually entered for the command parameter.
So the first two bytes of the parameter passed to the CPP will be automatically populated with the length of the string with the trailing blanks removed! That's why the CPP for the command is so simple—it needs to read only the first two bytes of the first parameter (to get the length) and copy it to the returned length parameter. Clever!
Once you've compiled this command and placed it in your library list, you can find out the length of any character string in CL as follows:
PGM
DCL VAR(&TEST) TYPE(*CHAR) LEN(100)
DCL VAR(&LEN) TYPE(*INT) LEN(2)
CHGVAR VAR(&TEST) VALUE('this is 9')
RTVDTALEN VAR(&TEST) DTALEN(&LEN)
ENDPGM
To calculate the maximum length of a string, DéesseT provided another command that's very similar:
Member RTVVARSIZ, file QCMDSRC:
CMD 'Retrieve size of CL variable'
PARM Var +
*X +
1 +
RTNVAL(*YES) +
MIN(1) +
VARY(*YES *INT2) +
PROMPT('CL variable name')
PARM Size +
*INT2 +
RTNVAL(*YES) +
MIN(1) +
PROMPT('Variable for SIZE (INT2)')
Member RTVVARSIZ, file QCLSRC:
PGM (&Var &Size)
DCL &Var *INT 2 /* CL variable name */
DCL &Size *INT 2 /* Size of CL variable */
CHGVAR &Size &Var
ENDPGM
The primary difference between this example and the previous one is that the first parameter is defined with RTNVAL(*YES). That makes a big difference, because when you specify RTNVAL(*YES), the CL program expects you to output data to that variable. It therefore sets the length equal to the maximum size of data that you can output (so you know how much space you have to work with).
Therefore, by using RTNVAL(*YES), you can calculate the maximum length of the character string instead of the trimmed length. Aside from that, it works the same as the previous example. You use it the same way, too:
PGM
DCL VAR(&TEST) TYPE(*CHAR) LEN(100)
DCL VAR(&LEN) TYPE(*INT) LEN(2)
CHGVAR VAR(&TEST) VALUE('this is 9')
RTVVARSIZ VAR(&TEST) SIZE(&LEN)
ENDPGM
I thought this was a pretty clever way to do it. You can read the full thread in the System iNetwork forums.
(Added March 12, 2010)
To compile the utilities in this article, please run the following commands:
CRTCLPGM PGM(RTVDTALEN) SRCFILE(QCLSRC)
CRTCMD CMD(RTVDTALEN) PGM(RTVDTALEN) SRCFILE(QCMDSRC)
ALLOW(*IPGM *BPGM *IMOD *BMOD)
CRTCLPGM PGM(RTVVARSIZ) SRCFILE(QCLSRC)
CRTCMD CMD(RTVVARSIZ) PGM(RTVVARSIZ) SRCFILE(QCMDSRC)
ALLOW(*IPGM *BPGM *IMOD *BMOD)
Folks, please make sure you are specifying the ALLOW parameter properly when you compile the commands. For example:
Hmm... I should add the compile instructions to the article.