New CL String-Handling Functions
Published: February 20, 2013
by Ted Holt
Note: The document accompanying this article is available for download here.
I love it when IBM enhances CL. When I hear of a new feature, my first thought is, "What can I do with this?" My mind has been spinning since Guy Vig first told me about CL's new string-handling functions a few weeks ago. Let me tell you about these functions and some of the uses for them that have already occurred to me.
First, here are a few facts you need to be aware of:
- IBM delivered the new functions in two PTFs: SI49061 and SI48166. The first one supersedes the second one, so you only need to install SI49061.
- You may use these functions in both OPM and ILE CL.
- You must compile under 7.1 to use these functions. IBM does not intend to make them available for previous releases. However, you may compile under 7.1 for 6.1 or V5R4, restore the compiled objects to systems that are running those releases, and the compiled code will run.
- These functions are permitted only in CL programs and procedures.
- According to the documentation Guy sent me, these functions "have almost the same behavior as the RPG built-in functions by the same name." I do not yet know where the differences lie.
The Trim Functions
The three trim functions return the values of CL variables without leading and/or trailing characters. I have used these functions many times in RPG and COBOL programs, and I am glad to have them in my CL tool chest.
The three trim functions are:
- %TRIM--trim, remove leading and trailing characters
- %TRIML--trim left, remove leading characters
- %TRIMR--trim right, remove trailing characters
The trim functions process two parameters: one required, one optional. The first parameter is the CL variable that contains the data. The second parameter contains one or more characters that are to be removed. It defaults to blanks.
You may use the trim functions wherever character expressions are allowed.
One obvious way to use %TRIM and %TRIMR is to replace the *TCAT operator. In this example, the trailing blanks are removed from &OBJLIB before the concatenation. After the CHGVAR runs, &QUALOBJ contains something like MYLIB/MYFILE.
dcl &ObjName *char 10
dcl &ObjLib *char 10
dcl &QualObj *char 21
chgvar var(&QualObj) +
value(%trim(&ObjLib) *cat '/' *cat %trim(&ObjName))
I don't have a problem with *TCAT, but I think this way is cleaner.
Another way I intend to use %TRIM and %TRIML is to strip leading blanks from character variables. IBM does not require me to enter character values left-adjusted into their screens, and I like to extend the same courtesy to the people who use my software. For example:
dcl &inObjname *char 10
dcl &ObjName *char 10
chgvar &ObjName %triml(&inObjName)
This beats the way I used to left-justify, which was to set up a loop that stripped off leading blanks, one character at a time.
The Scan Function
The %SCAN function returns the first position of a search string within another string. If the second string does not contain the first string, %SCAN returns zero.
The first parameter (the search argument) is the string you are looking for. It may be a character literal or variable. The second parameter (the source string) is the one being searched. It may be a character variable or the special value *LDA, for the job's local data area. Both parameters are required. The third parameter, which is optional, specifies the first position to be searched and defaults to one, i.e., the beginning of the string.
Imagine a command that allows someone to enter up to four option values into a single parameter.
cmd prompt('Do it')
parm kwd(Option) type(*char) len(8) rstd(*yes) +
values(*DETAIL *SUMMARY *NOTIFY *REBUILD) +
max(4) expr(*yes) prompt('Option')
The CL command-processing program receives a variable-length string of data. The first two bytes contain a signed integer that tells the number of values in the list. The rest of the string contains zero or more options. How do I determine which options were entered? Like this:
dcl &inOptList *char 34
dcl &OptList *char 32
dcl &ListLen *uint 2
dcl &Detail *lgl
dcl &Summary *lgl
dcl &Notify *lgl
dcl &Rebuild *lgl
chgvar var(&ListLen) value(%bin(&inOptList 1 2) * 8)
chgvar var(&OptList) value(%sst(&inOptList 3 &ListLen))
chgvar var(&Detail) value(%scan(*DETAIL &OptList) > 0)
chgvar var(&Summary) value(%scan(*SUMMARY &OptList) > 0)
chgvar var(&Notify) value(%scan(*NOTIFY &OptList) > 0)
chgvar var(&Rebuild) value(%scan(*REBUILD &OptList) > 0)
The program stores the length of the list (the number of values times the length of one value) into variable &LISTLEN.
The list is copied into variable &OPTLIST to provide a valid second parameter to %SCAN.
Each %SCAN returns a positive value if the sought-for option is in the list. In each case, the returned value is compared to zero, setting the logical variables to true or false.
The scan function makes easy work of setting the four logical variables to their proper values. Again, this beats my old method, which involved a loop and a select statement.
The Check Functions
The two check functions look for data that is not found in a string. %CHECK searches from left to right, and %CHECKR searches from right to left.
Like %SCAN, the check functions have two required and one optional parameter. The first parameter (the comparator string) is the set of allowable characters. The second parameter (the base string) is the string that is being searched. The last parameter (the optional one) is the position at which the search is to begin. It defaults to the beginning of the string for %CHECK and the end of the string for %CHECKR.
If the function finds a character in the second parameter that is not in the first parameter, it returns a positive value that indicates the position of the invalid character within the base string. If all the characters in the base string are found in the comparator string, the function returns zero.
I can see already that this function will eliminate some nasty code. Take a look at this IF statement:
if (&Option *eq 'E' *or &Option *eq 'e' +
*or &Option *eq 'N' *or &Option *eq 'n' +
*or &Option *eq 'P' *or &Option *eq 'p') do
Isn't that a beauty? (It would have been even uglier had I let SEU format it for me.) I've kept it to six values for this example, but I've seen much longer such conditions.
Compare that to this:
if (%check('EeNnPp' &Option) *eq 0) do
I Like It Already
I hope your appetite is whetted. I don't know what all I'll do with these functions, but to quote that great philosopher Ernest T. Bass, "I'm jus' gittin' started."
A million thanks to Guy Vig and Jennifer Liu of the CL compiler team. In case you’d like to read it, you may download the information Guy sent me in a document file here. It contains more detail and other examples. Also, IBM's description of the two PTFs at the following links: SI49061 and SI48166.
Post this story to del.icio.us
Post this story to Digg
Post this story to Slashdot