|Practical CL: The 32-Character Solution|
|Programming - CL|
|Written by Joe Pluta|
|Wednesday, 07 November 2012 00:00|
Since the System/38, we've had to deal with the implied 32-byte length of character parameters on the command line; this article provides the workarounds.
It doesn't matter where I'm working, how skilled the staff is, whether it's new development or mature legacy systems, it never fails that every couple of years I come across the old (and I do mean old) 32-character limitations on character parameters passed from the command line. It's a simple issue, really, but one that still manages to confound us. It may be the greatest example in our community of how those who cannot learn from history (or steal from those who have!) are doomed to repeat it.
What's the Problem?
The problem is simple. Take a program that expects parameters. Call that program from another program. Everything is wonderful! Typically, the system will just pass an address from one program to the other, and as long as the parameters are the same in both size and type on the two machines, everybody is happy. Even in these more modern days of prototypes and keywords like CONST, the compiler knows the size of the parameter, so it creates a variable of the correct size, and the call proceeds apace.
However, when you call a program from the command line, the command line has no way of knowing the size of the parameters in the called program. In that case, it creates a temporary variable, but it follows some set rules that were established back in the dawn of time. One of those rules is for character literals. Call a program this way:
CALL PGM(MYPGM) PARMS('HELLO')
The system will parse out the string HELLO and put it into a temporary variable. The size of the variable is either the size of the actual typed literal or 32, whichever is greater. Why 32? We don't know for certain, but if you really want to know, you can ask Wayne Evans; he wrote that portion of the code. In any event, 32 is the magic number. So why is this a problem?
Most of the time it isn't! If your parameter is 32 characters in length or less, the system will create a 32-character field and stuff your literal into it. The address of that temporary variable will be passed to the called program, and everything will be fine. The problem is when your parameter is longer than 32 characters. If you pass less than that (that is, one or more of the rightmost characters of your parameter are blank), then you will have a problem. In the case above, if the first parameter is 32 characters or less, you're fine. But if the first parameter is 50 characters, you end up with the situation shown in Figure 1.
Figure 1: If you enter a short parameter on the command line, it gets padded to only 32 characters.
As you can see, the first 32 characters are just fine. But the contents of the last 18 characters are unknown and in fact are likely to contain garbage, certainly not the blanks that you would expect or need. Now, you can get around this on a command line call by specifying the extra blanks. In this case, simply enclose the parameter value (HELLO) in quotes after being sure to follow it by 45 blanks. Counting out 45 blanks can be a pain, but if you're careful it will work.
Unless You're Submitting
I always remember the information in the preceding paragraphs. And yet I still end up stubbing my toe on this next bit, the submit. More specifically, the SBMJOB command. Take a look at the following simple program.
SBM001: PGM &PARM
DCL &PARM *CHAR 50
DCL &USER *CHAR 10
DCL &JOBTYPE *CHAR 1
RTVJOBA USER(&USER) TYPE(&JOBTYPE)
IF (&JOBTYPE *EQ '1') DO
SBMJOB CMD(CALL PGM(SBM001) PARM(&PARM))
SNDMSG MSG('SBM001: [' *CAT &PARM *CAT ']') TOUSR(&USER)
The code above shows the ubiquitous self-submitting CL. If you've never seen this before, you might want to take a brief peek. The program retrieves the job type using the RTVJOBA command. A value of '1' indicates that this is an interactive job, so the program submits a call to itself using the parameter that was passed in and then exits. Otherwise, the program is running in batch, which means the self-submitting part is done, so then it actually executes the business portion of the CL, which in this case is very trivial: use SNDMSG to send the passed-in parameter back to the caller. Trivial, yes, but it exemplifies the problem wonderfully.
You see, if I call this from the command line with a parameter containing less than 50 characters, I'll get garbage in the trailing characters of the message even if I pass the parameter into the program using quotes and padding it out with blanks to 50 characters! It's the latter part that makes people pull their hair out, but it's an utterly predictable result of a few things working in combination.
The first is what we've been battling all along here: passing a literal to a parameter of greater than 32 characters will get garbage if the calling parameter is not completely filled. "But," you say, "I padded the value with blanks!" Yes, you did, but there's where the second condition comes into play. You see, the CMD keyword of the SBMJOB command is not really meant for program-to-program calls. We use it because it's so darned convenient, but really the CMD keyword is nothing more than a wrapper around the RQSDTA parameter. It allows you to prompt for the command to execute, but in the end you may pay for the convenience in the fact that it reformats your command into the RQSDTA without regard for parameter lengths (since it doesn't know any!). In the case above, the RQSDTA ends up looking like this:
CALL PGM(MYPGM) PARMS('HELLO')
Which, as we now know, is just not going to work.
How Do I Fix It?
Two well-known workarounds for this problem exist. One involves creating an extra parameter that in this case would be 51 characters long and then concatenating a non-blank character onto the end of the input parameter into this longer parameter. The longer parameter is what gets sent to the SBMJOB command. It's rather Rube Goldberg-esque to me and a bit hard to explain, but it works. The simpler solution is to create a command definition that wraps around the CL; you specify the parameter length in the command and therefore get around the implicit 32-character limitation. The problem is that you have another object to maintain and possibly get out of sync.
This article though will give you what I consider to be a more elegant solution. If you recall, I mentioned that the CMD keyword is a wrapper for the RQSDTA parameter, but one that causes a problem for longer parameters. A way to avoid that problem is to not use the CMD keyword at all and instead use the RQSDTA parameter. You'll have to format the entire call command much the same way you would if you were building a parameter for QCMDEXC, but once you've done that (making sure to pad the input parameter properly), you no longer have to worry about the 32-character limitation. Here's the one-line change in my original program:
/* SBMJOB CMD(CALL SBM001 &PARMX) */
SBMJOB RQSDTA('CALL SBM001 ''' *CAT &PARM *CAT '''')
That's all it takes! Instead of using the CMD keyword and its promptable syntax, you just have to make sure to format your own command string. That can be tricky (especially if you have embedded quotes in your character strings), but absent that complication, this is a simple way to get around a tricky problem.
|Last Updated on Wednesday, 07 November 2012 00:00|