Guru: Passing Parameters From The Command Line
February 6, 2017 Jon Paris
In my previous “Fundamentals” tips, I discussed the importance of making sure parameters are the correct size and how to correctly handle omitted parameters. In this tip I want to address what has to be the most frequently asked question about parameters.
The most frequently asked parameter question comes in many forms, such as:
- “How do you pass them from the command line?”
- “Why do I see garbage in the back end of my character parameters?”
- “Why are my numeric values passed incorrectly?”
Usually these questions arise when a programmer wants to simplify the testing of a program and does not want to have to run a whole job or create a CL program in order to do so. As it turns out, the answer is also important to understand when you are submitting jobs for processing, since the parameters of the CMD parameter of the Submit Job (SBMJOB) command are effectively on the command line.
Before I try to answer these questions, there is a question I’d like you to ask yourself.
When a program call is performed, how does the system know the data types, and sizes of the parameters required? When you think about this remember that the only thing passed on a program call is a pointer to each parameter.
If your answer was “It doesn’t,” you are correct. If you didn’t get the correct answer, you might want to go back and read the first tip referenced above before continuing.
The only information stored in the *PGM object related to parameters are the minimum and maximum number that it can accept. In the case of RPG programs even this information is pretty useless, as the values are set to a minimum of 0 and a maximum of 255! There is zero information available about the type and size of the parameters themselves. (Check it out for yourself using DSPPGM if you doubt me.)
So, given that information, the system clearly has a “small problem” whenever parameters are passed from the command line. It knows nothing about the size and data types expected by the called program, so it does the only thing it can. It follows a set of conventions. The first is that, if the parameter is determined to be numeric, the command line processor converts it to a temporary 15,5 packed field and passes a pointer to that. If the parameter is character, then the temporary field is created as 32 characters in length or the actual length of the value passed, whichever is the larger.
For instance, let’s talk a look at a a command line call:
CALL ITJPGM ( 1.23 '456' 789 'A much longer value than any of the others')
The command will result in the creation of four temporary fields to hold the parameters. The first will be 15,5 packed, the second 32 characters, the third 15,5 packed, and the last one (assuming that I have counted correctly) 42 characters. Regardless of what the program was expecting, this is what it will get.
The potential for problems with numeric parameters should be fairly clear. For example if the program ITJPGM expects a packed value of 7,2 as the first parameter, then it is expecting to see the value of 1.23 represented as x’0000123F’. But the value is actually stored in a 15,5 field resulting in a value of x’000000000123000F’. So what the program actually sees is a value of x’00000000′ (i.e., zero) since it will only be looking at the first four bytes.
In the case of character fields, things are a little simpler. If the parameter is 32 characters or less, there is no issue. It will be passed left-aligned and blank-padded in a 32-character field. The fact that the program only processes the first n characters is not a problem. But what about the situation above, where I passed 42 characters? Suppose the program is expecting a 60-character field. The first 42 characters will be fine, but what follows them? The answer to that is: “Your guess is as good as mine.” It could be anything–and therein lies the problem. Suppose the logic of ITJPGM performs a trim operation on the field before performing further operations on it. If there is anything other than spaces after the 42nd character (and it is highly unlikely that there will only be spaces), then “garbage” will be in the back end of the field and will be processed as if you had keyed it in.
The problem in dealing with zoned parameters is that they have to be keyed in as quoted character strings. Not only that, but if the required value is negative, then you have to work out what the correct character representation of the final character should be, i.e. -1 = J, -2 = K, . . . -9 = R.
So, if you want to call programs from the command line, what is the solution? As is often the case “it depends.”
Probably the “correct” solution, and the one that will work under all circumstances, is to create a simple command to invoke the program. Because the command definitions include details of the parameter’s lengths and types the system knows how to format the data correctly for the call. You’ll also get a nice simple prompt screen should you care to use it. This is by far and away my preferred solution. Note however that this does not provide any assistance when dealing with zoned parameters since commands do not offer zoned as an available data type.
Here’s a simple sample that could be used for the program call described above.
CMD PARM NBR1 TYPE(*DEC) LEN(7 2) PARM SHORTCHAR TYPE(*CHAR) LEN(3) PARM KWD(NBR2) TYPE(*DEC) LEN(3) PARM LONGCHAR TYPE(*CHAR) LEN(60)
In the interests of simplicity I have not used any of the other nice features of commands such as default values, simple range checks, and more.
Another option is to pass numeric parameters as hexadecimal strings and to always make sure that any character parameters include the requisite number of blanks before the closing quote. Here’s an example of the same program call done this way:
CALL PGM(ITJPGM) PARM(X'0000123F' '456' X'789F' 'A much longer value than any of the others ')
I have seen this suggested as a solution many, many times. To me, though, this is just too much trouble and way too error prone. In particular it is difficult dealing with long character strings such as the 60-character field above because the basic command prompt does not allow for fields this long. To create this example I had to resort to using QCMD to be able to enter it. This approach might be OK if you want to test the program once with a single simple parameter, but if you want to run multiple tests, do yourself a favor and simply create a command.
There are many other options that people will suggest, but since most require changes in the existing program (such as defining all numerics as 15,5) I don’t find them terribly practical. If you want to study them then I will simply refer you to the midrange.com wiki entry on the subject, which you can find here.
Jon Paris is one of the world’s most knowledgeable experts on programming on the System i platform. Paris cut his teeth on the System/38 way back when, and in 1987 he joined IBM’s Toronto software lab to work on the COBOL compilers for the System/38 and System/36. He also worked on the creation of the COBOL/400 compilers for the original AS/400s back in 1988, and was one of the key developers behind RPG IV and the CODE/400 development tool. In 1998, he left IBM to start his own education and training firm, a job he does to this day with his wife, Susan Gantner–also an expert in System i programming. Paris and Gantner, along with Paul Tuohy and Skip Marchesani, are co-founders of System i Developer, which hosts the new RPG & DB2 Summit conference. Send your questions or comments for Jon to Ted Holt via the IT Jungle Contact page.