Midrange News for the IBM i Community


All About... QCMDEXC Published by: Bob Cozzi on 05 Apr 2011 view comments
© 2011 Robert Cozzi, Jr. All rights reserved.

© Robert Cozzi, Jr. All rights reserved. Reproduction/Redistribution prohibited.
A midrangeNews.com Publication

All About... QCMDEXC

QCMDEXC - The IBM i CL Command Processor

Why write about an API that has been on the operating system since the original version of our platform was launched back in 1978? Good question! The answer is because I recently noticed that of the top 10 things searched for on some of the discussion forums out there, QCMDEXC was running 5th or 6th. I thought WTH? (That's "what the heck" for our readers over 40.) It turns out that a number of new IBM i developers are working on code that you and I wrote, in some cases decades ago, and are running across RPG III or perhaps RPGIII that has been converted RPG IV that calls QCMDEXC.

Sponsored by: BCD

What is QCMDEXC?

This API is used to run CL commands within a high-level language program. The command runs at the same invocation level as the program that calls QCMDEXC. For example, if an OVRDBF command needs to be run from within an RPG program, you could (A) call a CL program, run the OVRDBF command and return or (B) call QCMDEXC and pass to it a field containing an OVRDBF command string.

The problem with option A is that the override (until recently) is lost upon return from the CL program. So option B--calling QCMDEXC to run the command at the same invocation--was required to retain that override; otherwise the override wouldn't actually apply. Today, the OVRDBF command supports the OVRSCOPE keyword that circumvents all that invocation nonsense, but there's a huge amount of pre-V2R3 code that still assumes the old invocation-sensitive OVRDBF exists (OVRSCOPE was introduced in V2R3.)

Of course not only can OVRDBF be run using QCMDEXC, but so can most other CL commands--commands such as CPYF, OPNQRYF, DLTF, DLTOVR, ADDLIBLE, CHGCURLIB, and host of others. The only restriction is that it must be a command that can be run from the Command Entry display.

Relatives

QCMDEXC has a couple of relatives, some long gone, such as QCAEXEC, and some that are still around, such as QCMDCHK and QCAPCMD, and one that is even easier to use, named "system" used in C programs. But QCMDEXC is the go-to API to run commands from within RPG (any version) so let's take a closer look at it.

QCMDEXC Parameters

Like any good RPG IV developer, when I think of calling an API, I think about prototyping it. I am unsure why IBM has never provided a single RPG IV prototype for any API--well I actually do know why, but I can't figure out how to say it without being sarcastic, so we'll save that for another day.

NOTE: Great News! In our next issue I reveal the first set of APIs for which IBM has ever shipped RPG IV prototypes. Don't miss the April 19th issue of RPG Report.

Before creating a prototype, we have to review the API's parameters. The parameter definitions for QCMDEXC are as follows:

  1. Command string to run - Char(32702)
  2. Command string length - Packed(15,5)
  3. Idiographic support (aka Double-byte character set support) - Char(3) - OPTIONAL

The Command string to run is the same format for as a command you would type into the Command Entry display or a Command Line menu--no difference--it does not however, support CL commands with embedded CL variables as are used in CL programs-only.

The Command string length is the length of the command string as it appears in the first parameter. Often programmers will pass the length of the field containing the command string to this parameter. This is, after all what the documentation says to do. However, I prefer to pass the command string length. The API works either way because QCMDEXC ignores trailing blanks in command strings.

Idiographic character support is a parameter that is rarely used. This 3-position character parameter is often omitted from the CALL to QCMDEXC and often not included in user-written prototypes. Its purpose is to inform QCMDEXC that the command string passed to it on parameter 1, contains double-byte characters. To inform QCMDEXC of double-byte characters, pass the upper case letters 'IGC' to this parameter. If the parameter is not passed, or anything other than 'IGC' is specified, the parameter is ignored. When writing a prototype, I prefer to include everything, including the IGC support parameter for QCMDEXC.

Legacy Calls to QCMDEXC

If you are working with legacy code, you may run across something similar to this RPGIII code.

.....C                     MOVEL'ADDLIBLE'CMD
     C                     MOVE 'XTOOLS'  CMD 
     C                     CALL 'QCMDEXC'              53
     C                     PARM           CMD   256
     C                     PARM  256      LENGTH 155

In this example, the ADDLIBLE XTOOLS command is performed by QCMDEXC.

Encountering the same thing in RPG IV, you might see something like the following:

.....C                   EVAL      cmd = 'ADDLIBLE XTOOLS'
     C                   EVAL      cmdLen = %len(%trimR(cmd))
     C                   CALL      'QCMDEXC'                            73
     C                   PARM                    CMD
     C                   PARM      CMDLEN        LENGTH           15 5

Both of these examples run the ADDLIBLE command from within RPG. But this brings up an obviously question, what about free format?

Calling QCMDEXC in Free Format RPG

Before moving to free format RPG IV syntax, calls to subprocedures and programs must be prototyped. As of version 7.1 (and after PTF SI42509 is installed) subprocedures that are implemented in "this" source member no longer require a prototype to be called from within "this" source member. While certainly a huge benefit (probably the best enhancement in RPG at v7.1) it does not apply to calls to external subprocedures or programs. For those, a prototype is always required.

Prototyping QCMDEXC

Prototyping QCMDEXC is easy and very forgiving. Most people have prototyped it inaccurately over the years, but it seems to work anyway. Here's the correct prototype for QCMDEXC.

..... ** Correct QCMDEXC API prototype                            
     D QCMDEXC         PR                  extPgm('QCMDEXC')
     D  cmdString                 32702A   Const OPTIONS(*VARSIZE)
     D  cmdLength                    15P 5 Const
     D  dbcs_IGC_Flag                 3A   Const OPTIONS(*NOPASS) 

Note the OPTIONS(*NOPASS) keyword on the dbcs_IGC_flag parameter. This means you may call QCMDEXC with 2 or 3 parameters; parameter 3 is optional (i.e., it does not have to be specified). So let's give it a try.

..... /free
           qcmdexc('ADDLIBLE LIB(XTOOLS)' : 20);
      /end-free

Certainly this is easier than using fixed format RPG, but I don't enjoy using magic numbers (hard-coded numeric literals) in my code. So specifying '20' for the length parameter feels wrong to me. A more agreeable design would be as follows:

.....D myCmd           S            256A   VARYING

      /free
           myCmd = 'ADDLIBLE LIB(XTOOLS) POSITION(*FIRST)';
           qcmdexc( myCmd : %LEN(myCmd));
      /end-free

Using the variable length field MYCMD, I copy the ADDLIBLE command (line 4) to MYCMD. This sets the MYCMD field's "current length" to the command string length. Then when I call QCMDEXC I can pass the MYCMD variable for parameter 1, and pass the result of the %LEN built-in function as parameter 2 (the command string length).

But the CMDSTRING parameter is not VARYING and CMDLENGTH is not an integer, how can it work?

Whenever a parameter definition includes the CONST keyword, the compiler will convert whatever data you pass to the parameter to the format required by that parameter. The only requirement is that the value being passed be the same primitive data-type (i.e., character or numeric) as the parameter. So a fixed-length character value or a variable length field may be passed to a VARYING character parameter that is also CONST. The other way around is also true, both fixed and variable length character fields may be passed to a fixed-length character parameter that is also CONST.

In our case, the 256 VARYING field is easily accepted by the 32704 fixed-length CONST parameter (it is converted by the compiler). Likewise, the 4-byte integer result of %LEN(MYCMD) is converted to a Packed(15,5) value before being passed to QCMDEXC--all courtesy of the CONST keyword.

The QCMDEXC Alternative - The system API

Case-sensitive APIs really screw up prose... The C language has a function that calls other programs and runs command-line functions including CL commands. That function is named system (it has a case-sensitive name) and is primarily used within C and C++. But RPG can use it as well, again if you write the prototype for it.

.....D system          PR            10I 0 extProc('system')
     D  cmdString                      *   Value OPTIONS( *STRING :*TRIM )

Obviously the system API is much easier to prototype than QCMDEXC, it has only 1 parameter--the command string.

The system API is a C runtime function, that may be called from within RPG IV by simply prototyping it. If you are compiling a program for a release of the operating system prior to v6r1 than you must also identify the C runtime library binding directory during the compile. The most convenient way to include C runtime library this by specifying the BNDDIR keyword on the RPG IV Header specification within the source member, as follows:

.....H BNDDIR('QC2LE')
     D system          PR            10I 0 extProc('system')
     D  cmdString                      *   Value OPTIONS( *STRING :*TRIM )

Remember, when a binding directory name is specified on the Header specification it must be in all upper case and enclosed in quotes. The biggest mistake people new to this feature make is to specify bnddir('qc2le') instead of bnddir('QC2LE'). Both compile just fine, but the binder (the 2nd phase of the compile process) will end with an error if you specify 'qc2le' instead of the uppercase 'QC2LE'. the good news is that when you move up to V6R1 and later, IBM has merged QC2LE with the default binding directory included in every compile. So BNDDIR('QC2LE') is no longer necessary.

Using the system API

Using the system API is effectively the same as QCMDEXC, you pass it the command to run, it runs the command and returns. There are a few caveats, however.

QCMDEXC records messages in the joblog from your command similar to the way a CL program will record joblog message. You can also retrieve messages using the messaging APIs from commands run with QCMDEXC. If either of those capabilities are important to you, use QCMDEXC. If not, consider using the simpler system API.

The system API returns the message ID of an exception message generated by running the CL command. That message is returned to a global variable that must be imported into your source member/module. In addition, the system API returns a zero (0) if the command ran successfully, or 1 if it did not. By testing the returned value for nto equal to 1 condition, you must then look at the exception message field.

Most RPG developers have not been exposed to the IMPORT keyword, so accessing the exception message ID may be a bit more complex than you are use to.  But it really isn't too complex, here's an example:

.....H BNDDIR('QC2LE')

     D cpfmsgid        S              7A   IMPORT('_EXCP_MSGID')
     
     D system          PR            10I 0 extProc('system')
     D  cmdString                      *   Value OPTIONS( *STRING :*TRIM )
      /free
           if (system('ADDLIBLE XTOOLS') = 1); // CPF error, cmd failed.
               // command failed, check CPFMSGID for the runtime error message ID
           endif;
      /end-free

The field named CPFMSGID contains the exception message that caused the CL command to fail. This might even been easier than using QCMDEXC in that respect. Note the declaration for CPFMSGID is an imported variable (imported from a service program listed in QC2LE) whose external name is '_EXCP_MSGID'. The IMPORT keyword is used to pull that variable into this RPG IV program. Imported field names are case sensitive, just like many API names. Fortunately, in this call, it is all upper case, however it does begin with an underscore--so make sure you code it correctly.

There is a warning that IBM gives when using the system API. The system API is not thread safe. Good to know if anyone ends up writing thread-dependent RPG that requires thread-safe CL commands. So use system, but if you have an issue, switch to QCMDEXC and see if you get different results. I don't think you will have any issues.

If you want the clean feel of the system API but prefer to use QCMDEXC, you can always write a wrapper and put it into your tools service program. I use RPG OPEN myself.

To hide the length parameter of QCMDEXC, you need to write a subprocedure that wraps QCMDEXC. My free RPGOPEN service program (download it at www.RPGOpen.com) includes such a wrapper for you. Here's what the code for RPGOpen's RUNCMD subprocedure looks like:

     H  NOMAIN OPTION(*NODEBUGIO:*SRCSTMT)
     H Copyright('RPG OPEN - (c) 2009-2011 Robert Cozzi, Jr. All rights reserved.')
      /IF NOT DEFINED(*V6R1M0)
     H BNDDIR('QC2LE')
      /ENDIF

      /INCLUDE RPGOPEN/QCPYSRC,cprotos
      /INCLUDE RPGOPEN/QCPYSRC,sysapi

     P runcmd          B                   EXPORT
     D runcmd          PI            10I 0
     D  cmdToRun                  32702A   Const Varying

      /free
           monitor;
             qcmdexc( cmdToRun : %len(cmdToRun) );
           on-error;
             return 0; // Failed :(   
           ENDMON;

           return 1;   // Probably worked! :)
      /end-free
     P runCmd          E
 

Calling the RUNCMD subprocedure instead of QCMDEXC or system means you are using the QCMDEXC API, but with the system API syntax--you only have to pass in the CL command to run. Once again we solve a subtle syntax complexity with a simple, easy to use, subprocedure.

You're welcome!

Call Me

Bob Cozzi accepts your questions for use in future RPG Report articles or content for midrangeNews.com. Topics of interest include: RPG IV, Web development with RPG IV, APIs, C/C++ or anything else IBM i development related (except subfiles, data areas and RPGII/III because Bob doesn't care about that stuff) write your questions using the Feedback link on the midrangeNews.com website. 

You can subscribe to RPG Report (we call it "follow") by visiting the RPG Report page on midrangeNews.com and then click the FOLLOW link in the table of contents for that page. To unsubscribe, simply click that same link. You must be signed up and signed in to midrangeNews.com to start following RPG Report.

Follow Bob Cozzi on Twitter

Return to midrangenews.com home page.
Sort Ascend | Descend

COMMENTS