The AS/400 example source code modules contained here are for educational purposes only. You may download them, compile them, test them, modify them and generally use them in any way you see fit as long as you understand that nothing here is claimed actually to work for any purpose other than educational and that these are not to be used as parts of commercial products.
These modules are intended to show various ways of using some of the AS/400 APIs. They are generally suitable for CL programming, but you should stay aware that they might not work in your (nor any) environment. For example, many modules had to be slightly modified just to get them loaded up onto the world-wide web. Such modifications could have introduced errors, not the least of which are typographical.
If you encounter an error, please notify me through the e-mail link on the initial page of this site. I will do what I can to make appropriate corrections.
Bear in mind that this site is often updated, either to add new items or to correct or modify existing ones.
If you can compile and use any of these modules, be my guest. Naturally, no rights are granted other than non-exclusive usage. Most especially, no right is granted that allows inclusion without my permission of any of these modules (nor any objects compiled from these modules) within any product intended for sale except as noted in the following paragraph. (Not that I expect anything here to be of significant monetary value, I just figure I should make that clear. If I thought these could make money, I sure wouldn't be sticking them on the web for anyone to grab.)
It wouldn't be worthwhile to present the command definition source for something like SNDDTAQE (Send Data Queue Entry) and then never have it be used by anyone. I've always been irritated by magazines, text books or technical manuals that present useful utilities or examples and then claim copyright protections; if I buy a magazine, I feel I'm buying the contents for use. Now, nothing here is presented for sale, but I don't mind if objects defined here happen to be used as building blocks elsewhere. An example of what wouldn't be appropriate would be to package up objects defined here and use them as a direct selling point or feature. That is, selling a package of 'User Space' or 'User Index' commands based directly on the related object definitions here would not be appropriate without permission. But the inclusion and use of SNDDTAQE in a utility that performs an additional useful and valuable function is not discouraged. The only requirement is that this web site ( http://zap.to/tl400 ) must be credited as a contributor in some reasonable way.
Overall, the rule is "What you see is what you get." You can view all source code before downloading -- if you don't like it, don't download it.
If a compilation fails due to a missing object, first verify that you have downloaded everything you need. See the index window for missing objects. If you cannot locate it on this site, send an e-mail through the link on the opening page and I will try to upload what you need or tell you where to find it. Bear in mind that this might not be possible for any number of reasons.
A command shell (command definition object -- CDO or *CMD) can simplify API usage in a CL program. A CDO describes and names parameters, as well as providing a mnemonic name for the API itself. It is also possible to define UIM help for a CDO which can effectively eliminate the need for a manual.
However, using a CDO instead of a direct CALL can introduce inefficiencies.
One inefficiency results from the requirement that CDO parms must have fixed length when calling APIs. (Although CDOs do allow variable length parms, each such parm is automatically prefixed by a length attribute. This physically alters the parm value making it unusable for the API.) This means every CLP must define all such parameters with the same DCL LEN() parameter.
An example is the Retrieve User Space (QUSRTVUS) API's receiver variable parameter. This parameter can have a length ranging from 1 through 16MB, but my RTVUSRSPC command limits it to precisely 1000 bytes. This is generally adequate because most list occurrences are less than 1K. Also, most elements I choose to place into user spaces can be broken into chunks less than 1K. For rare cases when 1K isn't enough, the command could be executed multiple times, grabbing up to 1K at a time; but it would be more efficient to directly CALL the API and grab exactly as much as needed.
Even when only a few bytes are needed, a 1K variable must be declared. This is the common case. You may want to retrieve 60 bytes, but the RCVVAR() variable still must be declared as *CHAR(1000). Fortunately, the CL compiler will always remind you. If you feel a different length is better, you can always code your command as you wish.
Also, many API parameters are data structures. Unfortunately, you cannot define a CDO parameter as a data structure for these APIs because there would be an "occurrences" attribute prefixed to the value. So, you must build the structure yourself, either within a work variable or by using a concatenation expression (which cannot include numeric values) for the parameter value. This may increase the programming effort in some cases.
Finally, in some cases, I've made decisions to define parameters as constants which are not always appropriate. An example is the ADDUSRIDXE command shell for the Add User Index Entries (QUSADDUI) API. The "Number of entries" parameter can range from 1 through 4095; but I set a constant value of <1>, allowing only a single entry to be added for each command execution. If you must add a block of entries, then you must either modify the CDO or CALL the API directly.
Note that the "error code" parameter is either coded to return exceptions to the caller or omitted entirely (which generally also returns exceptions to the caller). If you need a different action, you'll have to determine how you want it done and code it yourself. If you have what you think is a better way to code it, feel free to drop a line to the e-mail link and I'll see if it can be incorporated.
The CDO PARMs can act as translators, for example taking a packed *DEC variable from a CL program and converting it into a 4-byte binary field for the CPP or doing a similar translation for a numeric constant. CDO PARMs can also prescribe limits of numerous kinds. A *CHAR(1) field might be able to hold any single character value; but the PARM definition can enforce a limit such as allowing only the characters 'Y' and 'N' or only letters of the alphabet. A *DATE PARM will enforce valid dates including separators, all depending on the job DATFMT() and DATSEP() settings. These kinds of actions would otherwise require a lot of programming for a lot of programs if we didn't have the ability to create command shells.
But for this site, a primary purpose is to use CDOs to provide an alternative view of how AS/400 APIs are used. The command shells and their parms all have meaningful names, while a pure CALL command for one API will look very much like any other. By experimenting with the CDOs, you can learn how some APIs will act when called in different ways without having to learn every little detail of the APIs first. We can hide some of the complexity.
The following examples will illustrate some of this. In each case, the provided command shell performs the exact same function as its associated CALL statement (assuming CL variables have similar values where used).
CRTUSRIDX USRIDX(&USRIDXLIB/&FNCUSRIDX) ENTLEN(14) + KEYINS(*YES) KEYLEN(14) IMMUPD(*YES) CALL PGM(QSYS/QUSCRTUI) PARM(&USRIDX ' ' + 'F' X'0000000E' '1' X'0000000E' '1' '0' + '*ALL ' &TEXT)The CRTUSRIDX (Create User Index) command is one of the CDOs available here. It has QSYS/QUSCRTUI named as its CPP and this is the Create User Index API. Others might disagree, but I find the CRTUSRIDX form much easier to understand than the direct CALL, especially after seeing it a couple of times. Even after seeing the description of the parameters for QSYS/QUSCRTUI over and over again, I still can't be certain which parameter is being addressed by the '1' and the '0'.
RTVUSRIDXE ENTRTV(&ENT) ENTLENOFFS(&ENTLENOFFS) + NBRENTRTV(&NBRRTV) + USRIDX(&USRIDXLIB/&FNCUSRIDX) + SCHTYPE(*GT) SCHCTA(&PRVKEY) + SCHCTALEN(14) SCHCTAOFFS(1) CALL PGM(QSYS/QUSRTVUI) PARM(&ENT X'00000016' + &ENTLENOFFS X'00000010' &NBRRTV + '*NONE ' &USRIDX 'IDXE0100' + X'00000001' X'00000002' &PRVKEY + X'0000000E' X'00000001' X'00000000')Maybe RTVUSRIDXE and QSYS/QUSRTVUI is a better example. Even though variable names can be used in the QSYS/QUSRTVUI parameter list to help remember which parameter is for what, e.g., &PRVKEY, would you rather remember that a 4-byte binary value of x'00000002' is used to specify a "greater than" search? Or is it easier simply to specify SCHTYPE(*GT)? Come to think of it, when I look at the parameter values above, I'm not sure whether it's the X'00000001' or the X'00000002' that's going into the search-type parameter. Oh, well, we can always document it later.
There are two notable exceptions: First, I do create UIM Help modules for my commands; and I've uploaded a few samples to this site. I believe UIM Help is an excellent place to stash documentation. And second, many command parameters here translate mnemonic values into actual API values. For example, an API might be expecting values '0' and '1'; but my parameters might accept "*NO" and "*YES" and do the translation for you.
Further, once created, I recommend you run the CHGPGM OPTIMIZE() command and get as much optimizing as you can. There's not much point in using short-cuts like these if you don't go for the full benefits.