I have to confess that I had not used the QCAPCMD API prior to writing this post. So after some playing around with it this is what I found out about using it:
QCAPCMD uses two data structures which I need to explain. The first is called the "Option Control Block", and is used to pass values to the API to control how it behaves. Below is how I coded it in this example.
02 D OptCtlBlock DS 03 D 10I 0 inz(0) 04 D 1 inz('0') 05 D 1 inz('0') 06 D 1 inz('0') 07 D 4 inz(x'00000000') 08 D 10I 0 inz(0) 09 D 9 inz(x'000000000000000000')
I have not given the sub-fields names as I am not going to change or use the values of this data structure in any other way except to call QCAPCMD. The sub-fields are:
- Type of command processing. '0' (zero) means "command running", or I want to execute the command passed.
- DBCS data handling. '0' (zero) means I want to ignore DBCS data.
- Prompter action. '0' (zero) no prompting.
- Command string syntax. '0' (zero) means I will use the system (IBM i) syntax, rather than System/38 syntax (scary!).
- Message retrieve key. This is used during the prompting process. As I am not prompting I need to set this to 8 hexadecimal zeros.
- CCSID of command string. '0' (zero) means that I will use the CCSID of the job.
- Reserved. Not used but still has to be set to 18 hexadecimal zeros.
The next data structure we have to be concerned with is QUSEC. This is used by several APIs for returning error data. IBM has provided us with a source member for it that we can copy into this program.
10 D/copy qsysinc/qrpglesrc,qusec
If you look in that source member you find that QUSEC has the following sub-fields:
|QUSEI||Exception id (CPF error code)|
There is also a data structure called QUSC0200 in the copied source member. I have failed to find what it could be used for in this example.
In this example I am defining QCAPCMD as a procedure, with the EXTPGM keyword, therefore, I can call this program without having to leave free format. This is how I have defined the procedure prototype for it:
11 D QCAPCMD PR ExtPgm('QCAPCMD') 12 D 32702 options(*varsize) const 13 D 10I 0 const 14 D ocb like(OptCtlBlock) const 15 D 10I 0 const 16 D 8 const 17 D 32702 options(*varsize) const 18 D 10I 0 const 19 D 10I 0 20 D 32767 options(*varsize)
Yet again I have not given field names to the parameter fields, the exception is ocb as I have used the LIKE keyword I have to give it a name. The parameters are:
- Command string.
- Length of the command string.
- Option control block.
- Length of the option control block.
- Format of option control block.
- Changed command string. Used with prompting, not relevant in this example.
- Length available for changed command string. Used with prompting, not relevant in this example.
- Length of changed command string available to return. Used with prompting, not relevant in this example.
- Error code, data structure QUSEC.
Now we can put it all together into the program:
01 H dftactgrp(*no) * Option Control Block 02 D OptCtlBlock DS 03 D 10I 0 inz(0) 04 D 1 inz('0') 05 D 1 inz('0') 06 D 1 inz('0') 07 D 4 inz(x'00000000') 08 D 10I 0 inz(0) 09 D 9 inz(x'000000000000000000') * Error code parameter 10 D/copy qsysinc/qrpglesrc,qusec * Procedure definition for program QCAPCMD 11 D QCAPCMD PR ExtPgm('QCAPCMD') 12 D 32702 options(*varsize) const 13 D 10I 0 const 14 D ocb like(OptCtlBlock) const 15 D 10I 0 const 16 D 8 const 17 D 32702 options(*varsize) const 18 D 10I 0 const 19 D 10I 0 20 D 32767 options(*varsize) 21 D Command S 1000 varying 22 D ChgdCommand S 32702 varying 23 D Unused S 10I 0 /free 24 Command = 'CLRPFM QTEMP/TESTFILE' ; 25 QCAPCMD(Command: %len(Command): OptCtlBlock: %size(OptCtlBlock): 'CPOP0100': ChgdCommand: %len(ChgdCommand): Unused: QUSEC) ; 26 if (QUSEI <> ' ') ; . . 27 endif ; 28 *inlr = *on ;
The only part that is left to explain is the call of the procedure QCAPCMD, line 25. I have put each parameter on a seperate line to make it easier to understand. The parameters are:
- Command = Command string to clear the file QTEMP/TESTFILE.
- %len(Command) = Length of the string in the Command field.
- OpCtlBlock = Option control block data structure.
- %size(OptCtlBlock) Size of the option control block data structure.
- 'CPOP0100' = Format of option control block (C-P-O-P-zero-1-zero-zero).
- ChgdCommand = Not used, blank.
- %len(ChgdCommand) = As ChgdCommand is blank then this is zero.
- Unused = Not used, zero.
- QUSEC = Error code returned in the QUSEC data structure.
The built in functions %LEN and %SIZE are not the same. %LEN returns the length of the string in the field, %SIZE returns the total length. If we use these two built in functions on the Command field in the program:
x = %len(Command) ; y = %size(Command) ;
The results would be: x = 21 and y = 1002. y is 1002 as it is the length of the Command field, 1000 bytes, and 2 additional bytes as the field was defined as variable, varying.
Back to the program, on line 26 I check if the QUSEC sub-field QUSEI is not blank, if it is not then the command produced an error.
You can learn more about these from the IBM web site:
- Process commands (QCAPCMD) API
- ILE RPG Example of QCAPCMD API
- %LEN built in function
- %SIZE built in function
This article was written for IBM i 7.1, and it should work with earlier releases too.
Of the three methods I have described in this series of excuting CL commands in RPGLE programs, QCMDEXC, SYSTEM( ), or QCAPCMD I still prefer to use QCMDEXC with a MONITOR group as the easiest method to code and for others to understand.
What do you think? Add your thoughts below in the Comments secition of this post.