Professional Documents
Culture Documents
In a nutshell APIs provide a method for our applications written in a High Level Language to
interface with another program, a database, the operating system, or to provide some other
functionality without needing to be concerned with the logic or even the language the API uses.
The term Application Programming Interface (API) can be applied in many different types of
procedures and functions. APIs are programs that are written to be accessed by other programs.
They are not usually called from a command line, although a few can be. They take input from
parameters and their output is returned in parameters.
Many APIs provide our programs with system information or allow us to perform system
functions from within an application program. Others may provide information from within our
application system or perform complex tasks for our application.
If we write programs or service programs with functions that validate a product code, validate a
customer number, or calculate and return a price, these could be considered APIs. So we
actually write our own APIs everyday.
In this presentation we are going to focus on the APIs that are supplied by IBM for System i that
can help us in our daily development tasks or provide functionality that we can’t accomplish
reliably in our HLL programs. The most commonly used IBM supplied APIs are probably
Execute Command (QCMDEXC), Send Data Queue (QSNDDTAQ) and Receive Data Queue
(RCVDTAQ). You have used APIs if you have ever issued a call to any of these.
APIs where introduced to the AS400 with V1 R3. Although some existed before then they weren’t
called APIs. They where referred to as IBM Supplied Programs. QCMDEXC as one of these early
APIs. With V1 R3 they started calling them APIs. At that time there were about 600 APIs.
Around the time V5 R2 was released there where over 1,500 APIs. Today there are over 2,500.
IBM has enhanced the existing APIs as well as introducing new ones in every release sinve V1 R3.
API Types :
Original Program Model (OPM)
Integrated Language Environment (ILE)
UNIX-Type
ILE (Integrated Language Environment) APIs and UNIX APIs are typically procedure calls or function
calls within Service programs that are prototyped within your RPG program. Some need a Binding
directory added to the RPG program. Some are in QSYSINC and no additional Binding directory is
needed.
OPM APIs (Original Program Model) are usually executed through a standard static Call command or
operation code. These can be called from OPM programs or ILE programs.
Why Use APIs ?
In many cases APIs can allow access to system functions at a more detailed level than available
with CL commands. Many APIs allow us to gather information for processing within RPG
programs that we historically have used only in our Control Language programs.
IBM supplied APIs almost always outperform HLL programs, including Control Language
programs that perform the same tasks. This is because APIs have access to and use Machine
Interface (MI) instructions which are much more efficient than HLL instructions.
APIs can also allow access to system information and functions that are not available through
Control Language commands or RPG Operation Codes or Built in Functions. Some processing
can only be done using APIs. For example, accessing data in directories in the IFS can only be
done using APIs.
IBM supplies many APIs covering a wide range of functionality. We might as well take
advantage of the programming they have already done. If there is an API that does the job, why
reinvent the wheel? Using APIs can improve the productivity of our system as well as our
developers.
The more I use APIs in my RPG programs the less need I have for writing Control Language
Programs.
Where to Find the APIs Available
IBM provides over 2,500 Procedures and programs to your interface with the operating system
as well as performing complicated math and string functions.
API Documentation
API Documentation
The API documentation and parameter descriptions often seem complex. But, like subfile
programming, once you’ve mastered an API it’s easy to migrate it from one program to another.
Some of the descriptions are straight forward and can be translated directly.
CHAR(10) = 10A in RPG
PACKED(15,5) = 15P 5 in RPG
There are a couple of parameter data types that cause the most confusion.
BINARY(4), which does not translate to 4B 0 in RPG. BINARY(4) indicates a 4 byte binary integer, whereas 4B 0 in RPG
indicates a 2 byte binary field that contains four digits. In RPG this should be defined as (10I 0).
Don’t use the “B” data type in RPG. It’s not a true Binary Integer . The I and U data types are. The B data type is a leftover
data type from RPG III when there wasn’t an Integer data type. So, just avoid using the B data type altogether, in spite of the
fact that much of the data center documentation still uses them.
Optional Parameter:
3 IGC Process Control Input Char (3)
The Execute Command (QCMDEXC) API runs a single command. It is used to run a command from within a high-level
language (HLL) program or from within a CL program where it is not known at compile time what command is to be run or
what parameters are to be used.
QCMDEXC is called from within your HLL program and the command it runs is passed to it as a parameter on the CALL
command.
After the command runs, control returns to your HLL program.
Notes:
1. Command strings in System/38™ syntax can use the QCAEXEC API. The
QCAEXEC API accepts the same parameters as QCMDEXC.
Usage Notes
While this API is threadsafe, it should not be used to run a command that is not threadsafe in a job that has multiple threads.
Use the Display Command (DSPCMD) command to determine whether a command is threadsafe.
Error Messages
Errors can be retrieved from the program status data structure.
Message ID Error Message Text
CPF0005 E Returned command string exceeds variable provided length
CPF0006 E Errors occurred in command.
CPF3C90E Literal value cannot be changed.
CPF9872 E Program or service program &1 in library &2 ended. Reason code &3.
xxxnnnn E Any escape message issued by any command may be returned. The messages listed previously are those issued
by this API. Once the API has called the command analyzer, any message issued as an escape message may
appear.
API in existence prior to V1R3
The Unix type API documentation looks a little different. It is written with C & C++ programmers
being the primary target audience.
Description
The system() function passes the given string to the CL command processor for processing.
Return Value
If passed a non-NULL pointer to a string, the system() function passes the argument to the CL command processor. The
system() function returns zero if the command is successful. If passed a NULL pointer to a string, system() returns -1, and the
command processor is not called. If the command fails, system() returns 1. If the system() function fails, the global variable
_EXCP_MSGID in <stddef.h> is set with the exception message ID. The exception message ID set within the
_EXCP_MSGID variable is in job CCSID.
This program also includes use of C++ API’s from the QC2LE binding directory. I use the
System() API to execute AS400 commands instead of calling QCMDEXEC. For more
information on the C++ API’s provided in these service programs refer to the
C-Functions.pdf included with this presentation .
H DEBUG OPTION(*SRCSTMT)
H DFTACTGRP(*NO) ACTGRP(*NEW) BNDDIR('QC2LE': 'SSPC')
* -----------------------------------------------
* PROCEDURE PROTOTYPES
* -----------------------------------------------
D OpenDir pr * extproc('opendir')
D dirName * value options(*string)
D ReadDir pr * extproc('readdir')
D dirPtr * value
D p_DirEnt s *
D dsDirEnt ds based(p_DirEnt)
D d_Resrv1 16
D d_FGenId 10u 0
D d_FileNo 10u 0
D d_RcdLen 10u 0
D d_Resrv2 10i 0
D d_Resrv3 8
D d_NLSData 12
D nls_ccsid 10i 0 overlay(d_NLSData: 1)
D nls_cntry 2 overlay(d_NLSData: 5)
D nls_lang 3 overlay(d_NLSData: 7)
D nls_resrv 3 overlay(d_NLSData: 10)
D d_NameLen 10u 0
D d_FilName 640
p_Dir = OpenDir(rptPath);
if p_Dir <> *null;
if dec > 0;
extn = %subst(file: dec+1: d_NameLen-dec);
if extn = *blanks;
%subst(file: dec: 1) = ' ';
endif;
else;
extn = *blanks;
endif;
The following example of a menu system that allows menu option setup capabilities that uses
IBM supplied APIs to prompt for and syntax check commands entered for the menu options.
We will look at prompting for command entry as well as syntax checking commands entered.
Place cursor on the option line desired and press F4 for Attributes
Here is the code that processes the prompt and syntax checks the command entered:
The Program accepts an Action parameter that requests a prompt for the command or
verification on the syntax.
/*************************************************/
/** DECLARATIONS **/
/*************************************************/
DCL VAR(&CMD) TYPE(*CHAR) LEN(256)
DCL VAR(&RETCMD) TYPE(*CHAR) LEN(256)
DCL VAR(&ACTION) TYPE(*CHAR) LEN(1)
DCL VAR(&LEN) TYPE(*DEC) LEN(15 5)
DCL VAR(&MSGID) TYPE(*CHAR) LEN(7)
DCL VAR(&MSGF) TYPE(*CHAR) LEN(10)
DCL VAR(&MSGFLIB) TYPE(*CHAR) LEN(10)
DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(132)
DCL VAR(&MSGERR) TYPE(*CHAR) LEN(1)
DCL VAR(&FRSTCHAR) TYPE(*CHAR) LEN(1)
/*************************************************/
/** PROMPT FOR COMMAND ????????????? **/
/** THEN CHECK THE COMMAND FOR VALIDITY. **/
/*************************************************/
/*************************************************/
/** VERIFY COMMAND FORMAT ??????????????? **/
/*************************************************/
QCMDCHK will
IF COND(&ACTION *EQ 'V') THEN(DO) syntax check the
CALL PGM(QCMDCHK) PARM(&CMD &LEN) command entered.
MONMSG MSGID(CPF0006) EXEC(DO)
RCVMSG MSGTYPE(*DIAG) MSG(&MSGDTA) +
MSGID(&MSGID) MSGF(&MSGF) MSGFLIB(&MSGFLIB)
CHGVAR VAR(&MSGERR) VALUE('Y')
ENDDO
ENDDO
END: ENDPGM
The following API wasn’t available at the time this program was written or it could have been
used for the prompt, syntax check, and even execution.
See the APIs section of the Programming category of the iSeries Information Center
for information on the QCAPCMD API.
QUSCMDLN API
The easiest Command Line API of all is the QUSCMDLN API. This can be called from a Control
Language program or an RPG program. No binding directory is needed, no parameters are required.
Just execute a call. The results look like this:
This is the native IBM Command Line with F9 to retrieve the previous command and F4 for a prompt.
If you ever need to give users the ability to get to a command line using a Function Key this would be
the way to do it. This is generally used for programmer utilities.
Generate a Random Number within a range
CEERANO() Function
There are situations where a programmer may need to write a program to generate random
numbers.
For example, some company policies may dictate that employees be drug tested on a random
selection basis.
Let’s say the company has 260 employees. To try and cover all of the employees within a year
you would need to test 5 employees each week. You would probably set the Lower Limit at 1
and the Higher Limit at 260 (the number of employees). You could take the program below and
put it into a loop to generate 5 random numbers. Then having an employee list sorted by
employee number set the file pointer to the record number in the file corresponding to the
random numbers generated to generate your list for the week.
CEERAN0 takes as input an integer seed, which it updates each time to avoid repetitions in the
random number sequence, and outputs a double precision random number (if given 0 as a seed,
CEERAN0 will generate a random number based on the current system time).
The program sample illustrates how to produce random numbers.
The following example uses some predetermined fixed numbers but it’s easy to see how we
could get the number of active employee records from our employee master file and divide that
by 52 to get the number of employees we need each week.
Optional Parameter:
3 fc Output FEEDBACK
H DFTACTGRP(*NO) ACTGRP('QILE’)
d CEERAN0 PR
d seed 10I 0
d ranno 8F
d fc 12A options(*omit)
/free
range = (highno - lowno) + 1; The number
CEERAN0( seed : rand: *omit ); returned is in the
result = %int(rand * range) + lowno; rand variable.
dsply result;
*INLR = *On
return
/end-free
A signal is said to be delivered to a process when the specified signal-handling action for the
signal is taken. A signal is said to be accepted by a process when a signal is selected and
returned by one of the sigwait functions.
The sleep() API was introduced in V4 R2. The Signal APIs, including the Sleep() API are IBM
supplied APIs that are available whenever a program is compiled. Therefore no additional
binding directories need to be included in your RPG program. But, it does need to be
prototyped.
A sample RPG program using Sleep() :
HOPTION(*NODEBUGIO:*SRCSTMT)
H DFTACTGRP(*NO)
**********************************************************************
*
* Sleep Parameters
*
D Sleep PR 10I 0 ExtProc('sleep')
D Seconds 10U 0 Value
*
D Secd S 10U 0
*
*
*
**********************************************************************
*
*---------------------------------------------------------------------
* Calculation Specifications
*---------------------------------------------------------------------
*
*
*
* Wait for a number of seconds
*
C Eval Secd = 60
C CallP Sleep(Secd)
C Seton LR
QC2LE APIs
QC2LE is an IBM supplied Binding Directory that resides in QSYS. Within this binding
directory are about 25 Service Programs. These service programs have at least 250 C/C++
Unix type functions that can be used in RPG programs.
A list of these functions with their descriptions can be found in the C Functions V6R1.PDF on
the CD. You can also find the QC2LE APIs under the Unix type APIs in the API Finder.
As the RPG language has evolved and we have operation codes that give us better capabilities
with string manipulation and data type conversions. As a result many of these functions are
obsolete because we now have these built in functions in RPG. Others are low level file
handling functions that we would rarely use in RPG applications.
But, there are still some of these functions that can be very helpful in our daily development
tasks. So let’s look at a few of those.
The QC2LE Binding Directory must be included in the RPG program to make these functions
available. The functions to be used must also be prototyped.
Executing a system command with the system() Function
It's common to use the QCMDEXC API when you want to execute a CL command from an
RPG program. But you may find it more convenient to use a C runtime library function,
system(), to accomplish the same purpose.. You can simply pass the command string as a
parameter ,without the need to pass the length of the command string, or any other parameters
for that matter.
C Clear cmd
C Eval Cmd = 'OVRPRTF FILE(QPQUPRFIL) OUTQ(' +
C OUTQ + ') SAVE(*YES)'
C Eval Result = system(cmd) If the Result is zero the
command execution
was successful.
C Clear CMD
C Eval CMD = 'OVRPRTF FILE(QPQUPRFIL) OUTQ(' +
C OUTQ + ') SAVE(*YES)'
c Eval CMDLEN = %LEN(CMD)
C Call 'QCMDEXC'
C Parm CMD
C Parm CMDLEN
Validating Regular Expressions with the regexec() Function
This function can be used to verify the format of e-mail addresses, zip codes, credit card
numbers, and more.
The following example is a program written by Scott Klement to validate an email address that
uses not only the regexec() function but the regcomp() and regfree() functions as well.
In this example Scott includes some an error checking routine and uses the QMHSNDPM API
to generate an error message.
To Compile:
* CRTBNDRPG MAILCHK SRCFILE(xxx/QRPGLESRC) DBGVIEW(*LIST)
*
* To Run:
* CALL PGM(MAILCHK) PARM(&EMAILADDR &VALID)
*
* Note: Don't call from the command line, it'll only
* pass 32 characters! Call from another program,
* and pass a variable that's 100 chars long for
* the first parameter!
*
* To reset program (clear variables, free compiled copy
* of regular expression, etc.) call with no parameters:
*
* CALL PGM(MAILCHK)
*
*
H DFTACTGRP(*NO) BNDDIR('QC2LE')
/copy REGEX_H
D MAILCHK PR
D EmailAddr 100A const
D valid 1A
D MAILCHK PI
D EmailAddr 100A const
D valid 1A
D FatalError PR
D rc 10I 0 value
D reg likeds(regex_t)
D compiled s 1N inz(*OFF)
D pattern s 50A varying
D reg ds likeds(regex_t)
D match ds likeds(regmatch_t)
D rc s 10I 0
/free
// --------------------------------------------------
// If called with no parameters, clean everything
// up and exit the program.
// --------------------------------------------------
When done call the pgm
if (%parms = 0);
one last time with no
regfree(reg); parms to free thee
compiled = *Off; compiled pattern from
*inlr = *on; memory.
return;
endif;
// --------------------------------------------------
// Compile the regular expression
// (This is only done once, on the first call.)
//
// For more info about this E-mail address expresion
// see:
// http://www.regular-expressions.info/email.html
// --------------------------------------------------
if (not Compiled);
pattern = '^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$'; This is where the
pattern is
rc = regcomp( reg compiled into
: Pattern memory
: REG_EXTENDED + REG_ICASE + REG_NOSUB );
if rc <> 0;
FatalError(rc:reg);
Since *INLR is
endif;
not set to on if
compiled = *on; parms are passed
endif; compiled will be
*ON if called
// -------------------------------------------------- multiple times.
// Check the e-mail address against the regular
// expression.
// --------------------------------------------------
if (regexec( reg
: %trim(EmailAddr) regexec returns a
: 0 true or false
: match value.
: 0 ) = 0);
valid = *on;
else;
valid = *off;
endif;
return;
/end-free
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* FatalError(): Send exception message with error from
* regular expression routines.
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P FatalError B
D FatalError PI
D rc 10I 0 value
D reg likeds(regex_t)
D QMHSNDPM PR ExtPgm('QMHSNDPM')
D MessageID 7A Const
D QualMsgF 20A Const
D MsgData 512A Const
D MsgDtaLen 10I 0 Const
D MsgType 10A Const
D CallStkEnt 10A Const
D CallStkCnt 10I 0 Const
D MessageKey 4A
D ErrorCode 8192A options(*varsize)
D ErrorCode DS qualified
D BytesProv 1 4I 0 inz(0)
D BytesAvail 5 8I 0 inz(0)
D MsgKey S 4A
D Data s 512A varying
D Buf s 512A
/free
regerror(rc: reg: %addr(Buf): %size(buf));
Data = %str(%addr(buf));
QMHSNDPM( 'CPF9897'
: 'QCPFMSG *LIBL'
: Data
: %len(Data)
: '*ESCAPE'
: '*PGMBDY'
: 1
: MsgKey
: ErrorCode );
/end-free
P E
A copy of this program and the REGEX_H file are on the CD in a folder named validateEmail.
The Retrieve Member Description (QUSRMBRD) API is similar to the RTVMBRD command.
The Retrieve Object Description (QUSROBJD) API is similar to the RTVOBJD command.
The Retrieve Library Description (QLIRLIBD) API is similar to the RTVLIBD command.
The different formats allowed can be found in the documentation for the API through the API
Finder. Different formats include different data. The documentation also includes the different
fields within the formats and detailed descriptions of each field. Just pick the format that best
satisfies your needs. Remember, as with any other programming considerations, the less data
returned the more efficient you program is. So don’t just pick the format that returns ALL data
possible, pick the one that returns the data you need.
The data structures for these Retrieve API formats can be copied into your RPG programs from
QSYSINC/QRPGLESRC. The member name needed is the same name as the API.
These members are also heavily documented so this is another good resource for detailed
information.
A web site that has several good code examples of retrieve APIs is
http://www.think400.dk/apier_2.htm
Look at the code example for QUSRMBRD (Retrieve Member Description).
Look as well at the API documentation for this API in the IBM information center.
http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/index.jsp
Use the Find by Name option for API QUSRMBRD.
Be sure to look at the parameter groups as well as the layout of Format MBRD0100.
In the code sample the CallP operation has the format, the data structure to receive the data, and
the error code data structure spelled out as parameters. This program uses incoming parameters
for the Library, Source File name and Source member.
If no errors then the last change date for the member is returned.
List APIs
Many of our Control Language commands provide an *Outfile parameter. Historically when we,
as programmers, need a list of Physical Files, Logical Files, Library Names, etc. we use the
*Outfile option and then read through the output file to programmatically process each object.
For most commands that have a *Outfile option there is also a List API that will give your
program the same information.
The advantage to using a List API is that it runs so much faster than using the *Outfile method.
So, if you have a daily job that uses the *Outfile method it may be worth converting it to use a
List API. If you are writing a one-time job then simplicity of using the *Outfile may be
preferable.
The Open List APIs are used to return a list, usually a list of particular system object types
(Device descriptions, Spool Files, Jobs, etc). Also provided by IBM are APIs to process the
objects returned in the list.
Other APIs are provided allow you to retrieve lists with the attributes of objects from the Open
List APIs. The APIs to retrieve Physical / Logical file attributes is a good example of this.
The Open List APIs don’t require a user space like the other List APIs talked about below. Even
though an Open List API can return a lot of information, it returns this information into a
receiver variable. The reason for this technique is that Open List APIs return a “partial” list,
where you select which portion of the list you currently want to process. While your program is
processing this partial list that the API returned, the API goes out to get more information for
you to process. This sequence is referred to as asynchronous processing. So, the Open List
APIs are really geared towards or fast and efficient processing.
The other List APIs usually require an object referred to as a User Space to store the data listed.
User Spaces
The List APIs use an object called a User Space. A User Space is an object that is created on
disk but is processed with pointers like a memory object instead of a PF or LF. Think of it as a
Data Area for a list of records instead of a single value. Because it is written to disk it can also
be retrieved from one session to the next.
The Retrieve APIs and the List APIs return data into a data structure. The data structure depends on the
API and the Format being used. The code for the Data Structures needed reside as copy members in
QSYSINC / QRPGLESRC.
User Spaces are processed using pointers much the same way memory objects are.
Although all of the details of processing User Spaces and List APIs are beyond the scope of this
presentation programming examples can be found on the CD in the “Getting Started with APIs
from RPG.PDF”, the “RPG-APIs-Redpaper.PDF”, and the “Who Knew you could do that with
RPG IV.PDF” as well as online at the System i Info Center.
On The CD
This link is to the IBM i Information Center Index page. On this page there are links to the API
Finder or the i5 OS API page where there is additional API information as well as the API
Finder links.
There is also a link to System i CL Commands, System i PDFs, and the system i CL Finder.
http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/index.jsp
This is a link to the System i APIs for working with the IFS. Each API listed provides a link to
show the syntax and parameters for the API.
http://publib.boulder.ibm.com/infocenter/iseries/v5r4/index.jsp?topic=/apis/unix2
.htm
This site has a number of examples of different patterns for the regexec API.
http://www.regular-expressions.info/email.html