Have you ever called an RPG or CL program from a command entry program, passed parameters to it, and had a problem receiving the parameter's data? This problem started with the System/38 and still exists today.
The problem is that IBM had to decide how big a numeric value was going to default to when it was passed between the command entry display and a program. IBM chose Packed(15,5) as the default size for numeric parameter values and Char(30) for character parameter values.
This means that if you call a program from command entry or submit a call to the program via SBMJOB, the data specified for the parameters on that call are converted into temporary variables whose definition is, for numeric, Packed(15,5) and, for character, Char(30).
If the called program is expecting a numeric field in another size, you're going to see a data mapping error, or perhaps the data will pass through harmlessly if your program doesn't touch the parameter. But either way, you've got data defined differently than you intended.
This is why IBM used Packed(15,5) for the numeric parameters of such legacy APIs as QCMDEXC and QCMDCHK, which were frequently called from within CL programs.
With the huge influx of iSeries programmers over the last couple of years (outside North America and Europe), fundamental components of OS/400, such as creating a user-written CL command, are often overlooked as an easy solution.
A user-written CL command can solve this decades-old data mapping issue by providing a way to specify the type and length of each parameter being passed to a program. User-written CL commands are easy to create, but surprisingly are seldom implemented.
To create a user-written CL command, you first need a program that you want to call. This can be any program on your iSeries system, either written in-house or provided by a third party. A good example to illustrate this capability would be one of the OS/400 APIs, such as QSNDDTAQ (Send Data Queue).
The documentation for this API indicates that the first four parameters are required in order for the API to function properly. Those parameters are defined as follows:
QSNDDTAQ (Send Data Queue) Required Parameters | |
Parameter | Definition |
Data queue name (DTAQ) | Char(10) |
Data queue library (DTAQLIB) | Char(10) |
Length of data (DATALEN) | Packed(5,0) |
Data to queue (DATA) | Char(*) |
The third and fourth parameters are the two we need to focus on.
Length of data (DATALEN) is defined as a Packed(5,0) value. If we call this API from command entry, the value will be converted into Packed(15,5), regardless of what we specify as the numeric parameter. Clearly, this is not what the API needs.
Data to queue (DATA) is defined as Char(*). This type of parameter often confuses RPG programmers. It simply means that the parameter is character in nature, but the length of the parameter is not fixed; it is based on other settings. Often, this indicates that a format code is passed to the API along with other values. However, this API gets the length of the data to queue from the value specified on the third parameter.
To create a nice little user-written CL command that can call this API, you need to launch the source code editor of your choice and specify CMD for the SEU attribute.
To start a user-defined command, the first line must be the CMD statement. It specifies the prompt text that appears at the top of the display when the command is prompted. For example, the CPYF command, when prompted, contains the following on the first line:
To create something similar for the SNDDTAQ command, specify the following line of command definition source:
The next line is something I established back in the early 1980s as a standard, and it has been widely used ever since. All user-written commands call a program, but there is no place to specify what that program is except when you compile the command. So I like to insert the name of the program that is going to be called as a comment on the second line. Add the following to the command definition source:
The program that is called when our command is run is, in this example, the QSNDDTAQ API. Again, this is for documentation purposes.
The only thing left to do is create a definition for each of the corresponding parameters. The QSNDDTAQ API has multiple parameters, many of which are not required. So we can build a simple command definition with only the four parameters shown in the table above.
The first two parameters are fairly easy. They are Char(10) values, so to define them, you specify the following command definition statements:
PARM KWD(DTAQLIB) TYPE(*NAME) LEN(10) PROMPT(' Library')
Note that I used TYPE(*NAME). I could have used TYPE(*CHAR), but the special type of *NAME allows the command prompter to check the specified value at runtime to make sure it conforms to a valid OS/400 object name. It does not verify that the actual object exists, only that the name specified is syntactically correct. Other than that, TYPE(*CHAR) and TYPE(*NAME) are very similar.
But let's not just give the user a basic prompt. Why not do slightly more? Let's modify these two statements so that a little more checking is performed when data is specified on them. Change the above two statements to the following:
EXPR(*YES) PROMPT('Data queue name')
PARM KWD(DTAQLIB) TYPE(*NAME) LEN(10) DFT(*LIBL) +
SPCVAL((*LIBL) (*CURLIB)) EXPR(*YES) +
PROMPT(' Library')
I've added the EXPR(*YES) keyword along with MIN(1) on the DTAQ parameter. EXPR allows you to specify a value for the parameter in a CL program that includes *TCAT and similar expressions. Without this, only a CL variable or a literal would be supported. The MIN(1) keyword indicates that the parameter is required—that is, a value must be specified or the command will not run and the prompter will reject it.
On the DTAQLIB parameter, I've added the DFT(*LIBL) keyword, which allows you to specify a default value for the parameter. This means the parameter need not be specified, and if it isn't, this value is passed to your program automatically. In addition, since the TYPE(*NAME) keyword is used, things like *LIBL and *CURLIB would not conform to a valid name syntax-checking scheme, so I have to tell the command that it is OK to allow the two special values specified on the SPCVAL((*LIBL) (*CURLIB)) keyword. I've also included the EXPR(*YES) keyword as I did on the DTAQ parameter.
Let's look at the final two parameters, DATALEN and DATA.
PROMPT('Data length')
PARM KWD(DATA) TYPE(*X) LEN(1) +
PROMPT('Data')
The DATALEN parameter is a Packed(5,0) value. To define it as such, specify TYPE(*DEC) and LEN(5 0). TYPE(*DEC) is used to define packed parameters (there is no direct support for zoned decimal parameter). Some of the other data types supported include the following:
Supported Data Types | |
Command Parameter Type | RPG IV Definition |
*DEC | P (Packed) |
*LGL | N (Named indicator) |
*CHAR | A (Character) |
*INT2 | 5I0 (Integer) |
*INT4 | 10I0 (Integer) |
*NAME | A (Character) |
*GENERIC | A (Character) |
This is not a complete list, but it does include the data types most often used in command definitions.
The DATA parameter is a little different from the other parameters. Its data type is TYPE(*X), and its length is set to (1). TYPE(*X) is not normally used with user-written commands, but I've used it for decades without any issues.
This data type is polymorphic. It can be used when the data type is not known until runtime. Normally, other PARM command keywords, such as PASSATR, are specified to help the program being called understand what type of data is being passed. But in our simple user-written command, we're using it because the length of the DATA parameter is really not known until runtime. In addition, the length must be specified on the DATALEN parameter.
To give you a more complete example of the source code for our user-written command, I've summarized the entire source member below:
/* Command processing program is QSNDDTAQ */
PARM KWD(DTAQ) TYPE(*NAME) LEN(10) MIN(1) +
EXPR(*YES) PROMPT('Data queue name')
PARM KWD(DTAQLIB) TYPE(*NAME) LEN(10) DFT(*LIBL) +
SPCVAL((*LIBL) (*CURLIB)) EXPR(*YES) +
PROMPT(' Library')
PARM KWD(DATALEN) TYPE(*DEC) LEN(5 0) +
PROMPT('Data length')
PARM KWD(DATA) TYPE(*X) LEN(1) PROMPT('Data')
To compile this source code, use PDM option 14 or the CRTCMD command. (Yes, there's a Create Command command.) Be sure to specify the name of the command processing program (CPP) on the PGM parameter of the CRTCMD command, as follows:
User-written commands are tokenized and stored in a user space with the same name as the command. Well, it isn't strictly a user space, but it is a space object that is 99% the same as a user space.
Once your user-written command is compiled, go to command entry, type in SNDDTAQ, and press F4. You'll see a prompt similar to Figure 1:
Figure 1: SNDDTAQ gives you this prompt. (Click image to enlarge.)
To test this, I created a data queue in my library using CRTDTAQ and then ran the following command:
DATA('Watch iSeriesTV.com')
To verify that this works, I used the DMPOBJ command and dumped the data queue. Then I used WRKSPLF to display the spool file and saw the string "Watch iSeriesTV.com" buried in the output in the right-hand column.
It would be relatively easy to modify this command to include the keyed data queue parameters. In fact, it would only require the following two additional PARM statements:
DFT(*ZERO) SPCVAL((*ZERO 0)) +
PROMPT('Key length')
PARM KWD(KEYDATA) TYPE(*X) LEN(1) +
SPCVAL((*BLANKS ' ')) PROMPT('Key Data')
To add support for DDM data queues and their asynchronous option, the following parameter definition would be needed:
DFT(*NO) VALUES(*YES *NO) +
PROMPT('Async DDM data queue')
User-written commands are still cool and can be used to solve a lot of simple problems, such as passing bad data to your programs. Learn them.
Bob Cozzi is a programmer/consultant, writer/author, and software developer of the RPG xTools, a popular add-on subprocedure library for RPG IV. His book The Modern RPG Language has been the most widely used RPG programming book for nearly two decades. He, along with others, speaks at and runs the highly-popular RPG World conference for RPG programmers.
LATEST COMMENTS
MC Press Online