API's Examples
API's Examples
This article contains example programs that use APIs. It also contains examples of two exit programs. For additional information
concerning API examples, see the System API Programming manual on the V5R1 Supplemental Manuals Web site.
❍ OPM RPG
❍ OPM COBOL
❍ ILE C
2. The DLTOLDSPLF program is supplied in OPM RPG, OPM COBOL, and ILE C. It does the following:
■ QUSRTVUS API
■ QUSPTRUS API
d. Retrieves more spooled file attribute information received from the user space (QUSRSPLA API).
e. Calls the CLDLT program to delete the spooled files.
f. Sends a message to the user (QMHSNDM API).
g. Deletes the user space (QUSDLTUS API).
Note: The programs and source code used as examples in the spooled file portion of this article exist only in printed form. They are
not stored electronically on the iSeries server.
/****************************************************************/
/* */
/* CMD: DLTOLDSPLF */
/* */
/* LANGUAGE: CL COMMAND SOURCE */
/* */
/* DESCRIPTION: COMMAND SOURCE FOR THE DLTOLDSPLF COMMAND WHICH*/
/* INVOKES THE DLTOLDSPLF PROGRAM. */
/* */
/****************************************************************/
CMD PROMPT('DELETE OLD SPOOLED FILES')
/* PARAMETERS FOR LIST OF SPOOLED FILES (QUSLSPL) */
PARM KWD(USRPRFNME) +
TYPE(*SNAME) +
LEN(10) +
MIN(1) +
SPCVAL(*ALL) +
PROMPT('User Profile Name:')
PARM KWD(OUTQUEUE) +
TYPE(QUAL1) +
MIN(1) +
PROMPT('Output Queue:')
/* INFORMATION NEEDED FOR PROGRAM */
PARM KWD(DELETEDATE) +
TYPE(*DATE) +
PROMPT('Last Deletion Date:')
QUAL1: QUAL TYPE(*NAME) LEN(10) SPCVAL(*ALL)
QUAL TYPE(*NAME) LEN(10) SPCVAL(*LIBL *CURLIB ' ') +
PROMPT('Library Name:')
To delete old spooled files, you can use one of the application programs provided in the following languages:
● RPG
● COBOL
● ILE C
H* ***************************************************************
H* ***************************************************************
H* *
H* MODULE: DLTOLDSPLF *
H* *
H* LANGUAGE: RPG *
H* *
H* FUNCTION: THIS APPLICATION WILL DELETE OLD SPOOLED FILES *
H* FROM THE SYSTEM, BASED ON THE INPUT PARAMETERS. *
H* *
H* APIs USED: *
H* QUSCRTUS -- Create User Space *
H* QUSLSPLF -- List Spooled Files *
H* QUSRTVUS -- Retrieve User Space *
H* QUSRSPLA -- Retrieve Spooled File Attributes *
H* QMHSNDPM -- Send Program Message *
H* QUSDLTUS -- Delete User Space *
H* *
H* ***************************************************************
H* ***************************************************************
E/COPY QRPGSRC,EUSRSPLA
I 'NUMBER OF SPOOLED - C MSGTXT
I 'FILES DELETED: '
IMSGDTA DS
I 1 35 MSGDT1
I 36 400DLTCNT
ISTRUCT DS
I B 1 40USSIZE
I B 5 80GENLEN
I B 9 120RTVLEN
I B 13 160STRPOS
I B 17 200RCVLEN
I B 21 240SPLF#
I B 25 280MSGDLN
I B 29 320MSGQ#
I 33 38 FIL#
I 39 42 MSGKEY
I I 'DLTOLDSPLFQTEMP ' 43 62 USRSPC
I I '*REQUESTER ' 63 82 MSGQ
ITGTDAT DS
I 1 1 TGTCEN
I 2 3 TGTYR
I 4 5 TGTMTH
I 6 7 TGTDAY
I/COPY QRPGSRC,QUSGEN
I/COPY QRPGSRC,QUSLSPL
I/COPY QRPGSRC,QUSRSPLA
I*****************************************************************
I* The following is copied from QSYSINC/QRPGSRC member QUSEC
I* so that the variable length field QUSBNG can be defined
I* as 100 bytes for exception data. The defined field is
I* named EXCDTA.
I*****************************************************************
IQUSBN DS
I* Qus EC
I B 1 40QUSBNB
I* Bytes Provided
I B 5 80QUSBNC
I* Bytes Available
I 9 15 QUSBND
I* Exception Id
I 16 16 QUSBNF
I* Reserved
I* 17 17 QUSBNG
I* Varying length
I 17 116 EXCDTA
IDATSTR DS
I 1 1 DATCEN
I 202 203 DATYR
I 204 205 DATMTH
I 206 207 DATDAY
C* ***************************************************************
C* ***************************************************************
C* *
C* EXECUTABLE CODE STARTS HERE *
C* *
C* ***************************************************************
C* ***************************************************************
C* *
C *ENTRY PLIST
C PARM USRNAM 10
C PARM OUTQ 20
C PARM DLTDAT 7
C MOVE DLTDAT TGTDAT
C Z-ADD0 DLTCNT
C MOVE *BLANKS QUSBN
C Z-ADD0 QUSBNB
C* *
C* CREATE A USER SPACE TO STORE THE LIST OF SPOOLED FILES. *
C* *
C CALL 'QUSCRTUS'
C PARM USRSPC
C PARM *BLANKS USEXAT 10
C PARM 1024 USSIZE
C PARM ' ' USINIT 1
C PARM '*CHANGE 'USAUTH 10
C PARM *BLANKS USTEXT 50
C PARM '*YES 'USREPL 10
C PARM QUSBN
C* *
C* FILL THE USER SPACE JUST CREATED WITH SPOOLED FILES AS *
C* DEFINED IN THE CL COMMAND. *
C* *
C CALL 'QUSLSPL'
C PARM USRSPC
C PARM 'SPLF0100'FMTNM1 8
C PARM USRNAM
C PARM OUTQ
C PARM '*ALL 'FRMTYP 10
C PARM '*ALL 'USRDTA 10
C PARM QUSBN
C* *
C* THE USER SPACE IS NOW FILLED WITH THE LIST OF SPOOLED FILES. *
C* NOW USE THE QUSRTVUS API TO FIND THE NUMBER OF ENTRIES AND *
C* THE OFFSET AND SIZE OF EACH ENTRY IN THE USER SPACE. *
C* *
C Z-ADD140 GENLEN
C Z-ADD1 STRPOS
C* *
C CALL 'QUSRTVUS'
C PARM USRSPC
C PARM STRPOS
C PARM GENLEN
C PARM QUSBP
C PARM QUSBN
C* *
C* CHECK THE GENERIC HEADER DATA STRUCTURE FOR NUMBER OF LIST *
C* ENTRIES, OFFSET TO LIST ENTRIES, AND SIZE OF EACH LIST ENTRY. *
C* *
C Z-ADDQUSBPQ STRPOS
C ADD 1 STRPOS
C Z-ADDQUSBPT RTVLEN
C Z-ADD209 RCVLEN
C Z-ADD1 COUNT 150
C* *
C* ***************************************************************
C* ***************************************************************
C* *
C* BEGINNING OF LOOP (DO WHILE COUNT <= QUSBPS) *
C* *
C* ***************************************************************
C* *
C COUNT DOWLEQUSBPS
C* *
C* RETRIEVE THE INTERNAL JOB IDENTIFIER AND INTERNAL SPOOLED FILE*
C* IDENTIFIER FROM THE ENTRY IN THE USER SPACE. THIS INFORMATION*
C* WILL BE USED TO RETRIEVE THE ATTRIBUTES OF THE SPOOLED FILE. *
C* THIS WILL BE DONE FOR EACH ENTRY IN THE USER SPACE. *
C* *
C CALL 'QUSRTVUS'
C PARM USRSPC
C PARM STRPOS
C PARM RTVLEN
C PARM QUSFT
C PARM QUSBN
C* *
C* NOW RETRIEVE THE SPOOLED FILE ATTRIBUTES USING THE QUSRSPLA *
C* API. *
C* *
C MOVE *BLANKS JOBINF
C MOVEL'*INT' JOBINF 26
C MOVE QUSFTH QUSFXD
C MOVE QUSFTJ QUSFXF
C MOVEL'*INT' SPLFNM 10
C MOVE *BLANKS SPLF#
C* *
C CALL 'QUSRSPLA'
C PARM QUSFX
C PARM RCVLEN
C PARM 'SPLA0100'FMTNM2 8
C PARM JOBINF
C PARM QUSFXD
C PARM QUSFXF
C PARM SPLFNM
C PARM SPLF#
C PARM QUSBN
C* *
C* CHECK QUSFX DATA STRUCTURE FOR DATE FILE OPENED. *
C* DELETE SPOOLED FILES THAT ARE OLDER THAN THE TARGET DATE *
C* SPECIFIED ON THE COMMAND. A MESSAGE IS SENT FOR EACH SPOOLED *
C* FILE DELETED. *
C* *
C* *
C MOVE QUSFX7 DATSTR
C* *
C* GO BACK AND PROCESS THE REST OF THE ENTRIES IN THE USER *
C* SPACE. *
C QUSBPT ADD STRPOS STRPOS
C 1 ADD COUNT COUNT
C END
C* ************************************************************* *
C* ************************************************************* *
C* *
C* END OF LOOP *
C* *
C* ************************************************************* *
C* ************************************************************* *
C* *
C* AFTER ALL SPOOLED FILES HAVE BEEN DELETED THAT MET THE *
C* REQUIREMENTS, SEND A FINAL MESSAGE TO THE USER. *
C* DELETE THE USER SPACE OBJECT THAT WAS CREATED. *
C* *
C MOVELMSGTXT MSGDT1
C CALL 'QMHSNDM'
C PARM *BLANKS MSGID 7
C PARM *BLANKS MSGFIL 20
C PARM MSGDTA
C PARM 40 MSGDLN
C PARM '*INFO 'MSGTYP 10
C PARM MSGQ
C PARM 1 MSGQ#
C PARM *BLANKS RPYMQ 10
C PARM MSGKEY
C PARM QUSBN
C* *
C* DELETE THE USER SPACE OBJECT THAT WAS CREATED. *
C* *
C CALL 'QUSDLTUS'
C PARM USRSPC
C PARM QUSBN
C* *
C* *
C* ************************************************************* *
C* ************************************************************* *
C* *
C* END OF PROGRAM *
C* *
C* ************************************************************* *
C RETRN
C*
C* ************************************************************* *
C* *
C* CLDLT SUBROUTINE *
C* *
C* THIS SUBROUTINE CALLS A CL PROGRAM THAT WILL DELETE A SPOOLED *
C* FILE AND SEND A MESSAGE THAT THE SPOOLED FILE WAS DELETED. *
C* *
C* ************************************************************* *
C* *
C CLDLT BEGSR
C* *
C* KEEP A COUNTER OF HOW MANY SPOOLED FILES ARE DELETED. *
C* *
C ADD 1 DLTCNT
C MOVE QUSFXL FIL#
C CALL 'CLDLT'
C PARM QUSFXK
C PARM QUSFXJ
C PARM QUSFXH
C PARM QUSFXG
C PARM FIL#
C PARM QUSFXM
C PARM QUSFXN
C ENDSR
***************************************************************
* *
* PROGRAM: DLTOLDSPLF *
* *
* LANGUAGE: COBOL *
* *
* DESCRIPTION: DELETE OLD SPOOLED FILES *
* *
* APIs USED: QUSCRTUS, QUSLSPL, QUSRTVUS, QUSRSPLA, QUSDLTUS,*
* AND QMHSNDM. *
***************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. DLTOLDSPLF.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-AS400.
OBJECT-COMPUTER. IBM-AS400.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
COPY QUSGEN OF QSYSINC-QLBLSRC.
COPY QUSLSPL OF QSYSINC-QLBLSRC.
COPY QUSRSPLA OF QSYSINC-QLBLSRC.
*****************************************************************
* VALUES USED FOR ERROR CODE *
*****************************************************************
* The following is copied from QSYSINC/QLBLSRC member QUSEC
* so that the variable length field EXCEPTION-DATA can be defined
* as 100 bytes for exception data.
*****************************************************************
01 QUS-EC.
05 BYTES-PROVIDED PIC S9(00009) BINARY.
05 BYTES-AVAILABLE PIC S9(00009) BINARY.
05 EXCEPTION-ID PIC X(00007).
05 RESERVED PIC X(00001).
* 05 EXCEPTION-DATA PIC X(00001).
*
* Varying length
05 EXCEPTION-DATA PIC X(100).
***************************************************************
* VALUES USED FOR THE QUSCRTUS PROGRAM *
***************************************************************
01 CRTUS-INFO.
05 CRT-SPCNAME PIC X(20)
VALUE "DLTOLDSPLFQTEMP ".
05 CRT-EXTATTR PIC X(10) VALUE SPACE.
05 CRT-SPCSIZE PIC S9(9) BINARY VALUE 1024.
05 CRT-INITSPACE PIC X VALUE " ".
05 CRT-AUTHORITY PIC X(10) VALUE "*CHANGE ".
05 CRT-DESCRIPTION PIC X(50) VALUE SPACE.
05 CRT-USRRPL PIC X(10) VALUE "*YES ".
***************************************************************
* VALUES USED FOR THE QUSRTVUS PROGRAM *
***************************************************************
01 RTV-START-POS PIC S9(9) BINARY VALUE 1.
01 RTV-LENGTH PIC S9(9) BINARY VALUE 140.
***************************************************************
* VALUES USED FOR THE QUSLSPL AND QUSRSPLA PROGRAM *
***************************************************************
01 RSPLA-DATE.
05 R-CENTURY PIC X.
05 R-YEAR PIC X(2).
05 R-MONTH PIC X(2).
05 R-DAY PIC X(2).
01 LSPLA-FORMAT PIC X(8) VALUE "SPLF0100".
01 LSPLA-USERDATA PIC X(10) VALUE "*ALL ".
01 LSPLA-FORMTYPE PIC X(10) VALUE "*ALL ".
01 RSPLA-JOB-NAME PIC X(26) VALUE "*INT".
01 RSPLA-NAME PIC X(10) VALUE "*INT".
01 RSPLA-NUMBER PIC S9(9) BINARY VALUE -1.
01 RSPLA-FORMAT PIC X(10) VALUE "SPLA0100 ".
01 SPLA-VAR-LENGTH PIC S9(9) BINARY VALUE 800.
01 DLT-COUNT PIC 9(15) VALUE 0.
01 DLT-SPL-NUMBER PIC 9(6).
***************************************************************
* VALUES USED FOR THE QMHSNDM PROGRAM *
***************************************************************
01 MSG-ID PIC X(7) VALUE SPACE.
01 MSG-FL-NAME PIC X(20) VALUE SPACE.
01 MSG-DATA.
05 DATA-MD PIC X(34)
VALUE "NUMBER OF SPOOLED FILES DELETED : ".
05 DLT-NUM-MD PIC X(20) VALUE SPACE.
01 MSG-DATA-LEN PIC S9(9) BINARY VALUE 54.
01 MSG-TYPE PIC X(10) VALUE "*INFO ".
01 MSG-QUEUE PIC X(20)
VALUE "*REQUESTER ".
01 MSG-Q-NUM PIC S9(9) BINARY VALUE 1.
01 RPY-MSG PIC X(10) VALUE SPACE.
01 MSG-KEY PIC X(4) VALUE SPACE.
***************************************************************
* PARAMETERS THAT ARE PASSED TO THIS PROGRAM FROM THE COMMAND *
***************************************************************
LINKAGE SECTION.
01 PARM-USERNAME PIC X(10).
01 PARM-OUTQ PIC X(20).
01 PARM-DATE.
05 P-CENTURY PIC X.
05 P-YEAR PIC X(2).
05 P-MONTH PIC X(2).
05 P-DAY PIC X(2).
***************************************************************
* BEGINNING OF EXECUTABLE CODE. *
***************************************************************
PROCEDURE DIVISION USING PARM-USERNAME,
PARM-OUTQ,
PARM-DATE.
MAIN-PROGRAM.
* **********************************************************
* * INITIALIZE ERROR CODE STRUCTURE. *
* **********************************************************
* **********************************************************
* * CREATE THE USER SPACE USING INPUT PARMS FOR THE CALL *
* **********************************************************
* **********************************************************
* * LIST THE SPOOLED FILES TO THE USER SPACE OBJECT. *
* **********************************************************
* **********************************************************
* * RETRIEVE ENTRY INFORMATION FROM THE USER SPACE. *
* **********************************************************
* **********************************************************
* * IF ANY SPOOLED FILES WERE FOUND MATCHING THE SEARCH *
* * CRITERIA, RETRIEVE DETAILED INFORMATION AND DECIDE *
* * WHETHER TO DELETE THE FILE OR NOT. *
* **********************************************************
IF NUMBER-LIST-ENTRIES OF QUS-GENERIC-HEADER-0100
GREATER THAN ZERO THEN
ADD 1 TO OFFSET-LIST-DATA OF QUS-GENERIC-HEADER-0100
GIVING RTV-START-POS.
PERFORM CHECK-AND-DELETE THROUGH
CHECK-AND-DELETE-END NUMBER-LIST-ENTRIES
OF QUS-GENERIC-HEADER-0100 TIMES.
* **********************************************************
* * CALL THE QUSDLTUS API TO DELETE THE USER SPACE *
* * WE CREATED, AND TO SEND A MESSAGE TELLING HOW MANY *
* * SPOOLED FILES WERE DELETED. *
* **********************************************************
STOP RUN.
* **********************************************************
* * CHECK THE DATE OF THE SPOOLED FILE. IF IT IS OLDER *
* * OR EQUAL TO THE DATE PASSED IN, CALL THE PROCEDURE *
* * TO DELETE THE SPOOLED FILE. *
* **********************************************************
CHECK-AND-DELETE.
CALL "QUSRTVUS" USING CRT-SPCNAME,
RTV-START-POS,
SIZE-EACH-ENTRY OF
QUS-GENERIC-HEADER-0100,
QUS-SPLF0100,
QUS-EC.
* **********************************************************
* * ADVANCE TO NEXT SPOOLED FILE FOR PROCESSING THE CHECK *
* * AND DELETE. *
* **********************************************************
* **********************************************************
* * RETRIEVE THE ATTRIBUTES FOR THE SPOOLED FILE TO GET *
* * THE CREATE DATE FOR THE SPOOLED FILE. *
* **********************************************************
* **********************************************************
* * COMPARE THE CREATE DATE WITH THE DATE THAT WAS PASSED *
* * IN AS PARAMETER. *
* **********************************************************
CHECK-AND-DELETE-END.
* **********************************************************
* * THIS IS THE PROCEDURE TO DELETE THE SPOOLED FILE. *
* * ALL OF THE SPOOLED FILES WITH CREATE DATE OLDER OR *
* * EQUAL TO THE DATE PASSED IN AS PARAMETER WILL BE *
* * DELETED. *
* **********************************************************
DLT-SPLF.
ADD 1 TO DLT-COUNT.
MOVE SPLF-NUMBER OF QUS-SPLA0100 TO DLT-SPL-NUMBER.
DLT-SPLF-END.
To create the COBOL program, specify the following:
/*******************************************************************/
/* PROGRAM: DLTOLDSPLF */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* DESCRIPTION: THIS IS AN EXAMPLE PROGRAM FOR THE USE OF */
/* USER SPACES WRITTEN IN ILE C for OS/400. */
/* THE FLOW OF THIS PROGRAM IS AS FOLLOWS: */
/* (1) CREATE A USER SPACE USING QUSCRTUS */
/* (2) GET LIST OF SPOOLED FILES IN THE USER SPACE */
/* USING QUSLSPL */
/* (3) KEEP POINTER TO ENTRY LIST IN THE USER SPACE */
/* (4) ENTER LOOP */
/* RETRIEVE LIST ENTRY */
/* RETRIEVE MORE INFORMATION USING QUSRSPLA */
/* IF SPOOLED FILE IS TOO OLD */
/* DELETE SPOOLED FILE */
/* INCREMENT DELETE COUNTER */
/* END LOOP */
/* (5) DELETE USER SPACE */
/* */
/* APIs USED: QUSCRTUS, QUSLSPL, QUSRSPLA, QUSPTRUS, QUSDLTUS, */
/* QMHSNDPM, AND QMHSNDM. */
/* */
/*******************************************************************/
#include <string.h> /*strcpy, strncpy, strcmp */
#include <stdio.h>
#include <qusec.h> /*Error code structures */
#include <qusgen.h> /*General user space structures */
#include <quscrtus.h> /*Linkage info, structures for QUSCRTUS */
#include <quslspl.h> /*Linkage info, structures for QUSLSPL */
#include <qusptrus.h> /*Linkage info, structures for QUSPTRUS */
#include <qusrspla.h> /*Linkage info, structures for QUSRSPLA */
#include <qusdltus.h> /*Linkage info, structures for QUSDLTUS */
#include <qmhsndm.h> /*Linkage info, structures for QMHSNDM */
#include <qmhsndpm.h> /*Linkage info, structures for QMHSNDPM */
#pragma linkage(CLDLT,OS)
void CLDLT (char file_name[10],
char job_number[6],
char usr_name[10],
char job_name[10],
char file_number[6],
char form_type[10],
char usr_data[10]);
Qus_Generic_Header_0100_t *space;
char *list_section;
Qus_SPLF0100_t *entry_list;
Qus_SPLA0100_t *Rcv_Spl_Var;
/*****************************************************************/
/* PARMS FOR CLDLT */
/*****************************************************************/
char job_nmbr[6];
char usr_nm[10];
char job_nm[10];
char sp_job_name[10];
char sp_spl_number[6];
char File_Number[] = "*LAST ";
/*****************************************************************/
/* PARMS FOR QUSLSPL */
/*****************************************************************/
char frmt[8];
char usr[10];
char OutQ_Nm[20];
char ls_frm_typ[10];
char Usr_dat[10];
/*****************************************************************/
/* PARMS FOR QUSRSPLA */
/*****************************************************************/
char Rcv_Var[724];
int Rcv_lgth = 724;
char Rtv_Fmt[8];
char Qal_Jb_Nam[] = "*INT ";
char Splf_Name[] = "*INT ";
int Splf_Number = -1;
/*****************************************************************/
/* PARMS FOR QUSCRTUS */
/*****************************************************************/
char spc_name[20];
char ext_atr[10];
int initial_size;
char initial_value[1];
char auth[10];
char desc[50];
char replace[10];
/*****************************************************************/
/* PARMS FOR QMHSNDPM AND QMHSNDM */
/*****************************************************************/
char msg_id[7];
char msg_fl_name[20];
char msg_data[50];
int msg_data_len;
char msg_type[10];
char pgm_queue[10];
int pgm_stk_cnt;
char msg_key[4];
/*****************************************************************/
/* PARMS FOR QMHSNDM */
/*****************************************************************/
int msg_q_num;
char msg_queue[20];
char rpy_mq[10];
/*****************************************************************/
/* MISCELLANEOUS VARIABLES */
/*****************************************************************/
char pack_dlt_count[15];
int dlt_cnt;
int count;
char tmp_spl_number[7];
char dlt_date[7];
char spc_date[7];
int api_code;
Qus_EC_t err_code;
/*****************************************************************/
/* PROCEDURE TO CHECK THE ERRCODE RETURNED FROM CALLS TO APIs */
/*****************************************************************/
void error_check(void)
{
if (err_code.Bytes_Available != 0){
strncpy(msg_id,"CPF9898",7);
strncpy(msg_fl_name,"QCPFMSG *LIBL ",20);
strncpy(msg_data,"An error has occurred calling ",29);
switch (api_code){
case 1 : strncat(msg_data,"QUSCRTUS.",9);
case 2 : strncat(msg_data,"QUSLSPL. ",9);
case 3 : strncat(msg_data,"QUSPTRUS.",9);
case 4 : strncat(msg_data,"QUSRSPLA.",9);
case 5 : strncat(msg_data,"QUSDLTUS.",9);
case 6 : strncat(msg_data,"QMHSNDM. ",9);
default : strncat(msg_data,"UNKNOWN. ",9);
}
msg_data_len = 38;
strncpy(msg_type,"*ESCAPE ",10);
strncpy(pgm_queue,"* ",10);
pgm_stk_cnt = 1;
QMHSNDPM(msg_id,msg_fl_name,msg_data,msg_data_len,msg_type,
pgm_queue,pgm_stk_cnt,msg_key,&err_code);
}
}
/********************************************************************/
/* START OF MAINLINE */
/********************************************************************/
main(argc,argv)
int argc;
char *argv[];
{
/********************************************************************/
/* Read in and assign the command-line arguments to respective */
/* variables */
/********************************************************************/
strncpy(usr,argv[1],10);
strncpy(OutQ_Nm,argv[2],20);
strncpy(dlt_date,argv[3],7);
/********************************************************************/
/* Assign value to specific variables in the program */
/********************************************************************/
strcpy(spc_name,"DLTOLDSPLFQTEMP ");
memset(ext_atr,' ',10);
initial_size = 1024;
strcpy(initial_value," ");
strcpy(auth,"*CHANGE ");
memset(desc,' ',50);
strcpy(frmt,"SPLF0100");
strcpy(replace,"*YES ");
strcpy(ls_frm_typ,"*ALL ");
strcpy(Usr_dat,"*ALL ");
strcpy(Rtv_Fmt,"SPLA0100");
/********************************************************************/
/* Call external program to create a user space */
/********************************************************************/
err_code.Bytes_Provided = 0;
api_code = 1;
QUSCRTUS(spc_name,ext_atr,initial_size,initial_value,auth,desc,replace,
&err_code);
/********************************************************************/
/* Call external program to list spooled files into user space */
/********************************************************************/
api_code = 2;
QUSLSPL(spc_name,frmt,usr,OutQ_Nm,ls_frm_typ,Usr_dat,&err_code);
/********************************************************************/
/* Call external program to get a pointer to the user space */
/* and get addressability to the list data section. */
/********************************************************************/
api_code = 3;
QUSPTRUS(spc_name,&space,&err_code);
list_section = (char *)space;
list_section = list_section + space->Offset_List_Data;
entry_list = (Qus_SPLF0100_t *) list_section;
dlt_cnt = 0;
count = 1;
/********************************************************************/
/* Loop through the entry list and delete old spooled files */
/********************************************************************/
while (count <= space->Number_List_Entries) {
/********************************************************************/
/* Call external program to retrieve more spool information */
/********************************************************************/
api_code = 4;
QUSRSPLA(Rcv_Var,Rcv_lgth,Rtv_Fmt,Qal_Jb_Nam,
entry_list->Int_Job_ID,entry_list->Int_Splf_ID,
Splf_Name,Splf_Number,&err_code);
Rcv_Spl_Var = (Qus_SPLA0100_t *)Rcv_Var;
strncpy(spc_date,Rcv_Spl_Var->Date_File_Open,7);
/********************************************************************/
/* If spooled file is too old delete it */
/********************************************************************/
if (strncmp(spc_date,dlt_date,7) <= 0 ) {
strncpy(job_nm,Rcv_Spl_Var->Job_Name,10);
strncpy(job_nmbr,Rcv_Spl_Var->Job_Number,6);
strncpy(usr_nm,Rcv_Spl_Var->Usr_Name,10);
strncpy(sp_job_name,Rcv_Spl_Var->Splf_Name,10);
/********************************************************************/
/* Convert the spooled file number to character. */
/********************************************************************/
memcpy (sp_spl_number," ",6);
sprintf(tmp_spl_number,"%d",Rcv_Spl_Var->Splf_Number);
memcpy(sp_spl_number,tmp_spl_number,strlen(tmp_spl_number));
/********************************************************************/
/* Delete the spooled file. */
/********************************************************************/
CLDLT(sp_job_name,job_nmbr,usr_nm,
job_nm,sp_spl_number,ls_frm_typ,Usr_dat);
dlt_cnt++;
} /*IF*/
strcpy(spc_date," ");
count++;
entry_list++;
} /*WHILE*/
/********************************************************************/
/* Remove the user space */
/********************************************************************/
api_code = 5;
QUSDLTUS(spc_name, &err_code);
/********************************************************************/
/* Send final message to user indicating number of spooled files */
/* deleted. */
/********************************************************************/
api_code = 6;
strncpy(msg_id," ",7);
strncpy(msg_fl_name," ",20);
sprintf(msg_data,"Number of spooled files deleted: %d", dlt_cnt);
msg_data_len = strlen(msg_data);
strncpy(msg_type,"*INFO ",10);
strncpy(msg_queue,"*REQUESTER ",20);
msg_q_num = 1;
strncpy(rpy_mq," ",10);
QMHSNDM(msg_id,msg_fl_name,msg_data,msg_data_len,msg_type,
msg_queue,msg_q_num,rpy_mq,msg_key, &err_code);
/*********************************************************************/
/* */
/* PROGRAM: CLDLT */
/* */
/* LANGUAGE: CL */
/* */
/* DESCRIPTION: THIS PROGRAM WILL DELETE A SPECIFIC SPOOLED FILE */
/* USING THE DLTSPLF COMMAND AND SEND A MESSAGE WHEN */
/* THE FILE IS DELETED. */
/* */
/* */
/*********************************************************************/
/* */
PGM (&FILNAM &JOBNUM &USRNAM &JOBNAM &FILNUM &FRMTYP &USRDTA)
/* */
/* ***************************************************************** */
/* */
/* DECLARE SECTION */
/* */
/*********************************************************************/
/* */
DCL &FILNAM *CHAR 10
DCL &JOBNUM *CHAR 6
DCL &USRNAM *CHAR 10
DCL &JOBNAM *CHAR 10
DCL &FILNUM *CHAR 6
DCL &FRMTYP *CHAR 10
DCL &USRDTA *CHAR 10
MONMSG CPF0000
/* */
/*********************************************************************/
/* */
/* EXECUTABLE CODE */
/* */
/*********************************************************************/
/* */
DLTSPLF FILE(&FILNAM) +
JOB(&JOBNUM/&USRNAM/&JOBNAM) +
SPLNBR(&FILNUM) +
SELECT(&USRNAM *ALL &FRMTYP &USRDTA)
SNDPGMMSG MSG('Spooled file ' *CAT &FILNAM *CAT +
' number ' *CAT &FILNUM *CAT ' job ' +
*CAT &JOBNUM *CAT '/' +
*CAT &USRNAM *CAT '/' *CAT &JOBNAM *CAT +
' deleted.') +
TOUSR(*REQUESTER)
ENDPGM
To create the CL program, specify the following:
● The *ALL value was not specified for the JOB parameter.
● The *ALL value was not specified for the USER parameter.
The following is the message description needed for the Change Active Jobs (CHGACTJOB) command:
The following is the command-processing program that is written in CL to list the active jobs and reduce the run priority if necessary:
/* ***************************************************************** */
/* PROGRAM: CHGACTJOB */
/* */
/* LANGUAGE: CL */
/* */
/* DESCRIPTION: THIS PROGRAM WILL REDUCE THE RUN PRIORITY OF ACTIVE */
/* JOBS WITH THE SAME NAME. */
/* */
/* APIs USED: QUSCRTUS, QUSLJOB, QUSRTVUS, QUSRJOBI */
/* */
/* ***************************************************************** */
PGM PARM(&JOB &USER &RUNPTY)
/* */
/* Input parameters */
/* */
/* */
/* Local variables */
/* */
/* */
/* Start of executable code */
/* */
/* */
/* Retrieve job number to use for local user space name */
/* */
RTVJOBA NBR(&NUMBER)
CHGVAR VAR(%SST(&USRSPC 5 6)) VALUE(&NUMBER)
CHGVAR VAR(&EUSRSPC) VALUE(%SST(&USRSPC 1 10))
/* */
/* Delete user space if it already exists */
/* */
DLTUSRSPC USRSPC(QTEMP/&EUSRSPC)
MONMSG CPF0000
/* */
/* Create user space */
/* */
/* */
/* Set up job name for list jobs */
/* */
/* */
/* List active jobs with job name specified */
/* */
/* */
/* Retrieve number of entries returned. Convert to decimal and */
/* if zero go to NOJOBS label to send out 'No jobs' message. */
/* */
/* */
/* Retrieve list entry length, convert to decimal. */
/* Retrieve list entry offset, convert to decimal, and add one */
/* to set the position. */
/* */
CALL QUSRTVUS (&USRSPC X'00000089' X'00000004' +
&ELENB)
CHGVAR &ELEN %BINARY(&ELENB)
CALL QUSRTVUS (&USRSPC X'0000007D' X'00000004' +
&BIN4)
CHGVAR &DEC8 %BINARY(&BIN4)
CHGVAR VAR(&DEC8) VALUE(&DEC8 + 1)
/* */
/* Loop for the number of jobs until no more jobs then go to */
/* ALLDONE label */
/* */
/* */
/* Convert decimal position to binary 4 and retrieve list job entry */
/* */
/* */
/* Copy internal job identifier and retrieve job information for */
/* basic performance information. */
/* */
/* */
/* Copy job type and if subsystem monitor, spool reader, system job, */
/* spool writer, or SCPF system job then loop to next job */
/* */
/* */
/* Copy run priority, convert to decimal, convert to decimal 5,0, */
/* and if request run priority is less than or equal to the current */
/* run priority then loop to next job. */
/* */
/* */
/* Retrieve job name, convert to run priority to character, change */
/* the job run priority and seen message stating the run priority */
/* was changed. */
/* */
/* */
/* At end of loop set new decimal position to next entry and */
/* decrement loop counter by one. */
/* */
/* */
/* Send message that no jobs were found. */
/* */
/* */
/* All done. Now delete temporary user space that we created. */
/* */
The program can be changed to change the run priority by removing the IF statement to compare the current and requested run
priority.
● Specify different job attributes that the Change Job (CHGJOB) command can change.
● List only jobs on an output queue and remove the spooled files.
The following is the command-processing program that is written in CL to list the job schedule entries and change the user if
necessary:
/* **************************************************************** */
/* PROGRAM: CHGSCDEUSR */
/* */
/* LANGUAGE: CL */
/* */
/* DESCRIPTION: THIS PROGRAM WILL CHANGE THE USER FOR A LIST OF */
/* JOB SCHEDULE ENTRIES. */
/* */
/* APIs USED: QUSCRTUS, QWCLSCDE, QUSRTVUS */
/* */
/* **************************************************************** */
PGM PARM(&JOBNAME &OLDUSER &NEWUSER)
/* */
/* Input parameters are as follows: */
/* */
/* */
/* Local variables are as follows: */
/* */
/* */
/* Start of code */
/* */
/* */
/* You may want to monitor for additional messages here. */
/* */
/* */
/* This creates the user space. The user space will be 256 bytes */
/* and will be initialized to blanks. */
/* */
/* */
/* This lists job schedule entries of the name specified. */
/* */
/* */
/* Retrieve the generic header from the user space. */
/* */
/* */
/* Get the number of entries returned. Convert to decimal and */
/* if zero go to NOENTS label to send out 'No entries' message. */
/* */
/* */
/* Get the list entry length and the list entry offset. */
/* These values are used to set up the starting position. */
/* */
/* */
/* This loops for the number of entries until no more entries are */
/* found and goes to the ALLDONE label. */
/* */
/* */
/* This retrieves the list entry. */
/* */
CALL PGM(QUSRTVUS) PARM(&USRSPC &STRPOSB &ELENB +
&LENTRY)
MONMSG MSGID(CPF3C00) EXEC(GOTO CMDLBL(ERROR))
/* */
/* This copies the information status, job name, entry number, and */
/* user name. */
/* */
/* */
/* This checks to make sure the list entry contains the user name. */
/* If it does, the user name is compared to the old user name */
/* passed in. If either of these checks fails, this entry will */
/* be skipped. */
/* */
/* */
/* This code will issue the CHGJOBSCDE command for the entry. */
/* */
/* */
/* This sends a message that no entries were found. */
/* */
/* */
/* This sends a message that the list was incomplete. */
/* */
/* */
/* This sends a message that an unexpected error occurred. */
/* */
/* */
/* This will check for a partial list in the user space and */
/* finish processing the rest of the list. */
/* */
/* */
/* All done. Now the temporary user space is deleted. */
/* */
/********************************************************************/
/* PROGRAM: $USIDXCRT */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* DESCRIPTION: THIS PROGRAM CREATES A USER INDEX NAMED "TESTIDX" */
/* IN THE LIBRARY "QGPL". */
/* */
/* APIs USED: QUSCRTUI */
/* */
/********************************************************************/
#include <quscrtui.h>
main()
{
/********************************************************************/
/* Set up the parameters to be used in the call to 'QUSCRTUI' */
/********************************************************************/
char idx_name[]= "TESTIDX QGPL ";
char ext_atr[]= "TESTER ";
char entry_lgth_att[] = "F";
int entry_lngth = 50;
char key_insert[] = "1";
int key_lngth = 15;
char imm_update[] = "0";
char optim[] = "0";
char auth[] = "*CHANGE ";
char desc[] = "Description ..... ";
/********************************************************************/
/* Call the 'QUSCRTUI' program to create the user index. */
/********************************************************************/
QUSCRTUI(idx_name,ext_atr,entry_lgth_att,entry_lngth,key_insert,
key_lngth,imm_update,optim,auth,desc);
}
To compile the program that creates the user index, specify the following:
To insert entries into the user index, use the following ILE C program:
/********************************************************************/
/* PROGRAM: $USIDXEX */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* DESCRIPTION: THIS PROGRAM USES A USER INDEX TO KEEP TRACK OF */
/* NAMES AND PHONE NUMBERS. THERE ARE TWO OPERATIONS THAT ARE */
/* DEMONSTRATED IN THIS EXAMPLE. THE FIRST IS THE INSERTION OF */
/* AN ENTRY INTO THE INDEX, AND SECONDLY THE FINDING OF A GIVEN */
/* INDEX ENTRY. */
/* THE INDEX IS KEYED ON THE LAST NAME, THEREFORE ENTER AS MUCH */
/* OF THE NAME AS YOU KNOW AND THE PROGRAM WILL LIST ALL ENTRIES */
/* MATCHING YOUR STRING (IN ALPHABETICAL ORDER). */
/* */
/* APIs USED: NONE */
/* */
/********************************************************************/
#include <stdio.h>
#include <string.h>
#include <miindex.h>
#include <miptrnam.h>
#include <stdlib.h>
#include <ctype.h>
_SYSPTR index;
_IIX_Opt_List_T ins_option_list;
_IIX_Opt_List_T *fnd_option_list;
char Name_And_Num[50];
char In_Name[50];
char Out_Num[5000];
char response[1];
char name[35];
char number[15];
int Ent_Found,count,start,length_of_entry;
/********************************************************************/
/* Procedure to copy 'cpylngth' elements of 'string2' into the */
/* new string, 'string1'; starting at position 'strpos'. */
/********************************************************************/
void strncpyn(string1,string2,strpos,cpylngth)
char string1[],string2[];
int strpos,cpylngth;
{
int x = 0;
while (x < cpylngth)
string1[x++]=string2[strpos++];
} /*strncpyn*/
/********************************************************************/
/* Procedure to convert any string into uppercase, where applicable */
/********************************************************************/
void convert_case(string1)
char string1[];
{
int x = 0;
while (x < (strlen(string1))) {
string1[x] = toupper(string1[x]);
x++;
} /*while*/
} /*convert_case*/
main()
{
fnd_option_list = malloc(sizeof(_IIX_Opt_List_T)
+99*sizeof(_IIX_Entry_T));
/********************************************************************/
/* Resolve to the index created in $USIDXCRT. */
/********************************************************************/
index = rslvsp(_Usridx,"TESTIDX","QGPL",_AUTH_ALL);
/********************************************************************/
/* Set up the insert option list */
/********************************************************************/
ins_option_list.Rule = _INSERT_REPLACE;
ins_option_list.Arg_Length = 50;
ins_option_list.Occ_Count = 1;
ins_option_list.Entry[0].Entry_Length = 50;
ins_option_list.Entry[0].Entry_Offset = 0;
/********************************************************************/
/* Set up the find option list */
/********************************************************************/
fnd_option_list->Rule = _FIND_EQUALS;
fnd_option_list->Occ_Count = 100;
/********************************************************************/
/* Loop until the choice 'Q' is entered at the menu */
/********************************************************************/
while (1==1) {
printf("\n\n***********************\n");
printf("* TELEPHONE INDEX *\n");
printf("***********************\n");
printf("* 'A' Add name & num *\n");
printf("* 'L' List a number *\n");
printf("* 'Q' Quit index *\n");
printf("***********************\n");
gets(response);
if ((strncmp(response,"A",1)==0)||(strncmp(response,"a",1)==0))
{ printf("\nEnter name to add. ex(Last, First)\n");
gets(name);
convert_case(name);
printf("\nEnter number to add. ex(999-9999)\n");
gets(number);
strcpy(name,strcat(name," "));
strcpy(Name_And_Num,strcat(name,number));
printf("\nName and number to add is => %s\n",Name_And_Num);
insinxen(index,Name_And_Num,Integrated Netfinity Server_option_list);
} /* if 'a'*/
if ((strncmp(response,"L",1)==0)||(strncmp(response,"l",1)==0))
{
printf("\nEnter name to find. ex(Last, First)\n");
gets(In_Name);
convert_case(In_Name);
fnd_option_list->Arg_Length = strlen(In_Name);
fndinxen(Out_Num,index,fnd_option_list,In_Name);
length_of_entry = fnd_option_list->Entry[0].Entry_Length;
Ent_Found = fnd_option_list->Ret_Count;
if (Ent_Found == 0)
printf("\nName not found in index => %s\n",In_Name);
else {
if (Ent_Found > 1) {
printf("\n%d occurences found,\n",Ent_Found);
count = 0;
start = 0;
while (count++ < Ent_Found) {
printf("Name and number is => %s\n",Out_Num);
start = start + length_of_entry;
strncpyn(Out_Num,Out_Num,start,length_of_entry);
} /* while */
}else
printf("\nName and number is => %s\n",Out_Num);
} /*else*/
} /*if 'l'*/
if ((strncmp(response,"Q",1)==0)||(strncmp(response,"q",1)==0))
{ break; }
} /*while*/
} /*$USIDXEX*/
To create the ILE C program to insert entries into the user index, specify the following:
/********************************************************************/
/* */
/* PROGRAM: GLOBALV */
/* */
/* LANGUAGE: MI/IRP */
/* */
/* DESCRIPTION: MAINTAINS AN INDEPENDENT INDEX. EACH INDEX ENTRY */
/* CONTAINS 100 BYTES OF USER DATA. THE ENTRIES ARE */
/* KEYED TWO 10 BYTE VALUES: THE USER PROFILE AND A */
/* VALUE IDENTIFIER. */
/* */
/* APIs USED: QUSCRTUI */
/* */
/* PARAMETERS: */
/* */
/* PARM TYPE DESCRIPTION */
/* */
/* 1 CHAR(1) FUNCTION: */
/* */
/* 'U': UPDATE GLOBALV INFORMATION */
/* 'R': RETRIEVE GLOBALV INFORMATION */
/* */
/* 2 CHAR(10) USER PROFILE */
/* */
/* THE NAME OF THE USER PROFILE FOR WHICH */
/* INFORMATION IS TO BE SAVED OR RETRIEVED. */
/* */
/* 3 CHAR(10) VALUE ID */
/* */
/* THE NAME OF THE GLOBALV VARIABLE ID FOR WHICH */
/* INFORMATION IS TO BE SAVED OR RETRIEVED. */
/* */
/* 4 CHAR(100) VALUE */
/* */
/* IF FUNCTION IS 'U', THIS VALUE SHOULD CONTAIN */
/* THE NEW VALUE TO BE ASSOCIATED WITH THE */
/* USER ID AND VALUE ID. */
/* */
/* IF FUNCTION IS 'R', THIS VARIABLE WILL BE */
/* SET TO THE VALUE ASSOCIATED WITH THE USER ID */
/* AND VALUE ID. IF NO VALUE EXISTS, *NONE */
/* IS SPECIFIED. */
/* */
/********************************************************************/
/********************************************************************/
/* PARAMETER VALUE POINTERS FOR GLOBALV. */
/********************************************************************/
/********************************************************************/
/* PARAMETER VALUES FOR GLOBALV. */
/********************************************************************/
/********************************************************************/
/* PARAMETER LIST FOR GLOBALV. */
/********************************************************************/
/********************************************************************/
/* ARGUMENT VALUES FOR CREATE USER INDEX (QUSCRTUI) API. */
/********************************************************************/
/********************************************************************/
/* POINTERS TO ARGUMENT VALUES FOR QUSCRTUI API. */
/********************************************************************/
/********************************************************************/
/* ARGUMENT LIST FOR QUSCRTUI API. */
/********************************************************************/
/********************************************************************/
/* SYTSEM POINTER TO QUSCRTUI API *PGM OBJECT. */
/********************************************************************/
/********************************************************************/
/* SYSTEM POINTER TO GLOBALV *USRIDX OBJECT. */
/********************************************************************/
/********************************************************************/
/* EXCEPTION MONITOR TO DETECT 2201X EXCEPTIONS (OBJECT NOT FOUND) */
/********************************************************************/
/********************************************************************/
/* PASA INVOCATION ENTRY FOR RETURN FROM EXCEPTION. */
/********************************************************************/
/********************************************************************/
/* RECEIVER VARIABLE FOR INDEPENDENT INDEX OPERATIONS. */
/********************************************************************/
/********************************************************************/
/* OPTION TEMPLATE FOR INDEPENDENT INDEX OPERATIONS. */
/********************************************************************/
/********************************************************************/
/* ARGUMENT VARIABLE FOR INDEPENDENT INDEX OPERATIONS. */
/********************************************************************/
NOT_UPDATE:
FOUND_ENTRY:
NOT_RETRIEVE:
/********************************************************************/
/* "OBJECT NOT FOUND" EXCEPTION HANDLER. */
/********************************************************************/
ENTRY CREATE_INDEX INT;
/********************************************************************/
/* PROGRAM: $USQEXREQ */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* DESCRIPTION: THIS PROGRAM ENTERS COMMANDS TO BE PROCESSED ONTO */
/* A QUEUE CALLED 'TESTQ' IN LIBRARY 'QGPL'. THE USER WILL BE */
/* PROMPTED TO ENTER AS MANY COMMANDS (UNDER 51 CHARACTERS) AS */
/* IS DESIRED. WHEN THE USER WISHES TO END THE PROGRAMS, */
/* ALL THAT NEED BE DONE IS ENTER 'quit' AT THE PROMPT. */
/* */
/* APIs USED: */
/* */
/********************************************************************/
#include <stdio.h>
#include <string.h>
#include <miqueue.h>
#include <miptrnam.h>
main()
{
_ENQ_Msg_Prefix_T e_msg_prefix;
_SYSPTR queue;
char INMsg[100];
/********************************************************************/
/* Resolve to the queue created by $USQEXSRV. */
/********************************************************************/
queue = rslvsp(_Usrq,"TESTQ","QGPL",_AUTH_ALL);
e_msg_prefix.Msg_Len = 100;
/********************************************************************/
/* Loop until the user enters 'quit' as the command. */
/********************************************************************/
while (1) {
printf("\nEnter command to put on queue, or 'quit' \n ");
scanf("%100s", INMsg);
gets(INMsg);
printf("\nCommand entered was ==> %.100s\n",INMsg);
/********************************************************************/
/* Check to see if the user entered 'quit' as the command. */
/* If true then break out of the 'while' loop. */
/********************************************************************/
/********************************************************************/
/* Add the user-entered command to the queue. */
/********************************************************************/
enq(queue,&e_msg_prefix,INMsg);
strcpy(INMsg," ");
} /*while*/
/********************************************************************/
/* Add the command end to the queue which causes the */
/* server program ($USQEXSRV) to end */
/********************************************************************/
strcpy(INMsg,"END");
enq(queue,&e_msg_prefix,INMsg);
} /* $USQEXREQ */
/********************************************************************/
/* PROGRAM: $USQEXSRV */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* DESCRIPTION: THIS PROGRAM EXTRACTS COMMANDS TO BE RUN FROM */
/* A QUEUE CALLED 'TESTQ' IN LIBRARY 'QGPL'. THE COMMANDS WILL */
/* BE EXTRACTED AND RUN IN FIFO ORDER. THE QUEUE WILL BE */
/* CREATED PRIOR TO USE AND SHOULD BE DELETED AFTER EACH USE */
/* OF THIS EXAMPLE PROGRAM. THIS PROGRAM END WHEN IT */
/* EXTRACTS THE COMMAND 'END' FROM THE QUEUE. */
/* THE FLOW IS AS FOLLOWS: */
/* (1) CREATE THE USER QUEUE */
/* (2) ENTER LOOP */
/* (3) WAIT FOREVER FOR A COMMAND ON THE QUEUE */
/* (4) IF COMMAND IS 'END' THEN EXIT LOOP */
/* (5) ELSE RUN COMMAND, RESTART LOOP */
/* (6) END LOOP */
/* FOR BEST RESULTS, THIS PROGRAM CAN BE CALLED BY THE USER, THEN*/
/* THE $USQEXREQ SHOULD BE CALLED FROM ANOTHER SESSION. */
/* */
/* APIs USED: QCMDEXC, QUSCRTUQ */
/* */
/********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <micomput.h>
#include <miqueue.h>
#include <miptrnam.h>
#include <quscrtuq.h>
#include <qcmdexc.h>
main()
{
_DEQ_Msg_Prefix_T d_msg_prefix;
_SYSPTR queue;
char OUTMsg[100];
int cmd_name_lngth;
decimal(15,5) pack_name_lngth;
char igc_param[] = "IGC";
/********************************************************************/
/* Set up the parameters to be used in the call to 'QUSCRTUQ' */
/********************************************************************/
/********************************************************************/
/* Call the 'QUSCRTUQ' program to create the user queue. */
/********************************************************************/
QUSCRTUQ(q_name,ext_atr,q_type,key_lngth,max_msg_s,int_msgs,
add_msgs,auth,desc);
/********************************************************************/
/* Resolve to the queue created above. */
/********************************************************************/
queue = rslvsp(_Usrq,"TESTQ","QGPL",_AUTH_ALL);
/********************************************************************/
/* Set the deq operation to wait for command indefinitely. */
/********************************************************************/
d_msg_prefix.Wait_Forever = 1;
/********************************************************************/
/* Loop until the command 'END' is extracted from the queue */
/********************************************************************/
while (1) {
deq(&d_msg_prefix,OUTMsg,queue);
/********************************************************************/
/* Check to see if the command extracted is 'END' */
/* If true then break out of the 'while' loop. */
/********************************************************************/
if (strncmp(OUTMsg,"END",3) == 0)
{ break; }
cmd_name_lngth = strlen(OUTMsg);
/********************************************************************/
/* Convert the integer in cmd_name_lngth to a packed decimal */
/********************************************************************/
cpynv(NUM_DESCR(_T_PACKED,15,5), &pack_name_lngth,
NUM_DESCR(_T_SIGNED,4,0), &cmd_name_lngth);
/********************************************************************/
/* Execute the command extracted from the queue */
/********************************************************************/
QCMDEXC(OUTMsg,pack_name_lngth,igc_param);
} /* while */
} /* $USQEXSRV */
Defining Queries
This topic includes several examples that use the Query (QQQQRY) API. The examples define the following query functions:
● A simple query to perform ordering
● A join query
The following QQAPI header (or include) file and the QQFUNCS query code are used by all the examples. The example programs
follow the QQAPI header and QQFUNCS code.
QQAPI Header
/********************************************************************/
#ifndef _QQAPIH
#define _QQAPIH
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: Defines constants and structures for use */
/* with the QQQQRY API examples. */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: None */
/* */
/*******************************************************************/
/*******************************************************************/
/* The following define will enable some debug procedures and code */
/* #define QQDEBUG */
/* simple defines */
#define ON 1
#define OFF 0
/* Function prototypes: */
void dumpPtr(char *, char *, int );
char *strcnv400(char *, int );
int strcpy400(char *, char *, int );
void initUFCB(QDBUFCB_T *, int , Qdb_Qddfmt_t *);
void initQDT(QDBQH_T *, char , int , int ,
char , int );
void initFile(QDBQFHDR_T *, char , char );
void initFormat(Qdb_Qddfmt_t *, char *);
void initSelection(QDBQS_T *);
void initOrderBy(QDBQKH_T *);
void initGroupBy(QDBQGH_T *);
void initJoin(QDBQJHDR_T *);
int addFile(QDBQFHDR_T *, QDBQN_T *,
char *, char *, char *, char *);
int getRecordFmt(Qdb_Qddfmt_t *, long,
char *, char *, char *);
long copyField(Qdb_Qddfmt_t *, char *, int ,
Qdb_Qddfmt_t *);
void setFieldUsage(Qdb_Qddfmt_t *, char *, char );
int addSelectField(QDBQS_T *, char *, int );
int addSelectLiteral(QDBQS_T *, void *, int );
int addSelectOperator(QDBQS_T *, char *);
int addOrderBy(QDBQKH_T *, QDBQKF_T *,
char *, int );
int addGroupBy(QDBQGH_T *, QDBQGF_T *,
char *, int );
int addJoinTest(QDBQJHDR_T *, QDBQJFLD_T *, char *,
int , char *, int , char *);
void addQDTsection(QDBQH_T *, char *, int , int *);
long createAccessPlanSpace(ACCPLN_T *, char *, long );
int saveQDT(QDBQH_T *, ACCPLN_T *);
int saveAccessPlan(ACCPLN_T *);
int loadQDT(QDBQH_T *, ACCPLN_T *);
long loadAccessPlan(ACCPLN_T *, char *);
#endif
/********************************************************************/
/********************************************************************/
#include <stdio.h>
#include <string.h>
#include <qdbrtvfd.h>
#include <qqqqry.h>
#include <quscrtus.h>
#include <qusptrus.h>
#include <quscusat.h>
#include <qusrusat.h>
#include "qqapi.h"
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: This module contains all of the functions */
/* used by the examples to build the API information. */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: QDBRTVFD, QUSCRTUS, QUSCUSAT, QUSPTRUS, QUSRUSAT */
/* */
/*******************************************************************/
/*******************************************************************/
#ifdef QQDEBUG
/* dumpPtr(comment string, pointer, length)
- prints a comment then dumps data in hexadecimal starting at the
given pointer location for the specified length */
void dumpPtr(char *text, char *ptr, int len)
{
int i;
printf("%s\n", text);
for (i=0; i < len; i++, ptr++)
{
printf("%02X ", (int) *ptr);
if ((i+1) % 16 == 0)
printf("\n");
}
printf("\n");
}
#endif
/* verify parameters */
if (ufcbPtr == NULL || openFlags == 0)
{
printf("Invalid UFCB settings\n");
return;
}
/* Clear the entire UFCB */
memset((void *) ufcbPtr, (char) 0, sizeof(QDBUFCB_T));
/* Now start initializing values */
ufcb = &ufcbPtr->qufcb;
strcpy400((char *) ufcb->relver.release, REQ_REL,
sizeof(ufcb->relver.release));
strcpy400((char *) ufcb->relver.version, REQ_VER,
sizeof(ufcb->relver.version));
/* Blocked Records (BLKRCD) should be on if CPYFRMQRYF is used */
ufcb->markcnt.flg2brcd = ON;
ufcb->parameter.maximum = MAXFORMATS;
/* Set the open option */
if (openFlags&QO_INPUT)
ufcb->open.flagui = ON;
if (openFlags&QO_OUTPUT)
ufcb->open.flaguo = ON;
if (openFlags&QO_UPDATE)
ufcb->open.flaguu = ON;
if (openFlags&QO_DELETE)
ufcb->open.flagud = ON;
/* set up options to match _Ropen options */
ufcb->parameter.keyfdbk = KEYFDBK;
ufcb->parameter.keyonoff = ON; /* Key feedback ON */
ufcb->parameter.filedep = FILEDEP;
ufcb->parameter.fldonoff = ON; /* File dependent I/O ON */
/* turn the rest of the parameters off */
ufcb->parameter.seqonly = NOTSEQUPROC;
ufcb->parameter.primrln1 = NOTRECORDLTH;
ufcb->parameter.commitc = NOTCOMITCTL;
/* if the format is supplied,
define it in the UFCB and do level checking */
if (formatPtr != NULL)
{
ufcb->parameter.lvlchk = LEVELCK;
ufcb->parameter.lvlonoff = ON; /* Level check ON */
ufcb->parameter.curnum = 1; /* only one format */
/* set the format name and format level identifier */
ufcb->parameter.recfmts = FORMATSEQ;
memcpy(ufcb->parameter.formats[0].name, formatPtr->Qddfname,
sizeof(ufcb->parameter.formats[0].name));
memcpy(ufcb->parameter.formats[0].number, formatPtr->Qddfseq,
sizeof(ufcb->parameter.formats[0].number));
}
else /* no format and level checking */
{
ufcb->parameter.lvlchk = NOTLEVELCK;
ufcb->parameter.recfmts = NOTFORMATSEQ;
}
ufcb->ufcbend = ENDLIST;
}
/* initQDT(qdt, options...)
- initialize the QDT header */
void initQDT(QDBQH_T *qdtHdr, char alwCpyDta,
int optAllAp, int statusMsgs,
char optimize, int forRows)
{
if (qdtHdr == NULL)
{
printf("Invalid QDT settings\n");
return; /* invalid pointer */
}
/* Clear the entire QDT */
memset((void *) qdtHdr, (char) 0, sizeof(QDBQH_T));
/* set the initial QDT space used size */
qdtHdr->qdbspcsize = sizeof(QDBQH_T);
/* QDT options... */
/* ordering not specified */
qdtHdr->qdbqkeyo = -1;
/* set optimize parameter (ALLIO, FIRSTIO, MINWAIT) */
if (optimize == QDBQFINA || optimize == QDBQFINF ||
optimize == QDBQFINM || optimize == QDBQFINC)
qdtHdr->qdbqfin = optimize; /* OPTIMIZE() parameter */
else
qdtHdr->qdbqfin = QDBQFINA; /* default to OPTIMIZE(*ALLIO) */
/* set allow copy data parameter (YES, NO, OPTIMIZE) */
if (alwCpyDta == QDBQTEMN || alwCpyDta == QDBQTEMO ||
alwCpyDta == QDBQTEMA)
qdtHdr->qdbqtem = alwCpyDta; /* ALWCPYDTA() parameter */
else
qdtHdr->qdbqtem = QDBQTEMA; /* default to ALWCPYDTA(*YES) */
/* status messages (YES, NO) */
qdtHdr->qdbqattr.qdbqnst = statusMsgs ? ON : OFF;
/* optimize all access path parameter (YES, NO) */
qdtHdr->qdbqdt_7.qdbqopta = optAllAp ? ON : OFF;
/* optimizer for n rows parameter */
qdtHdr->qdbq_optmrows = forRows > 0 ? forRows : 0;
}
/* initSelection(selection section)
- initialize the selection header section */
void initSelection(QDBQS_T *selectHdr)
{
if (selectHdr == NULL)
{
printf("Invalid selection settings\n");
return; /* invalid pointer */
}
/* Clear the header */
memset((void *) selectHdr, (char) 0, sizeof(QDBQS_T));
/* set initial selection spec size (minus dummy selection spec) */
selectHdr->qdbqsl = sizeof(QDBQS_T) - sizeof(selectHdr->qdbqspec);
}
/* initOrderBy(orderby section)
- initialize order by header section */
void initOrderBy(QDBQKH_T *orderByHdr)
{
if (orderByHdr == NULL)
{
printf("Invalid Order By settings\n");
return; /* invalid pointer */
}
/* Clear the header */
memset((void *) orderByHdr, (char) 0, sizeof(QDBQKH_T));
}
/* initGroupBy(groupby section)
- initialize group by header section */
void initGroupBy(QDBQGH_T *groupByHdr)
{
if (groupByHdr == NULL)
{
printf("Invalid Group By settings\n");
return; /* invalid pointer */
}
/* Clear the header */
memset((void *) groupByHdr, (char) 0, sizeof(QDBQGH_T));
}
/* initJoin(join section)
- initialize join header section */
void initJoin(QDBQJHDR_T *joinHdr)
{
if (joinHdr == NULL)
{
printf("Invalid Join settings\n");
return; /* invalid pointer */
}
/* Clear the header */
memset((void *) joinHdr, (char) 0, sizeof(QDBQKH_T));
/* set initial join spec size */
joinHdr->qdbqjln = sizeof(QDBQJHDR_T);
}
/* addFile (file section, file spec section, file name, file library,
file member, file format)
- add file information to the file section */
int addFile(QDBQFHDR_T *fileHdr, QDBQN_T *fileSpec,
char *filename, char *library, char *member, char *format)
{
int i;
QDBQFLMF_T *fileSpecPtr;
if (formatPtr == NULL)
return; /* missing data */
if (fieldName != NULL)
strcpy400(padField, fieldName, 30);
/* set up field pointers */
fieldPtr = (Qdb_Qddffld_t *) (formatPtr + 1);
/* loop through all the fields, looking for a match */
for (i=0; i < formatPtr->Qddffldnum; i++,
fieldPtr = (Qdb_Qddffld_t *) ((char *) fieldPtr +
fieldPtr->Qddfdefl))
/* if all fields to be set or a match was found... */
if (fieldName == NULL ||
memcmp(fieldPtr->Qddfflde, padField, 30) == 0)
fieldPtr->Qddffiob = usage;
}
errcode.bytes_provided = 512;
return(0);
}
/* saveAccessPlan(access plan)
- update the size in the access plan (QQQQRY actually wrote the data) */
int saveAccessPlan(ACCPLN_T *accessPlan)
{
_SPCPTR usrSpcPtr;
errcode.bytes_provided = 512;
errcode.msgid[0] = (char) 0;
return(accessPlan->size);
}
/********************************************************************/
/********************************************************************/
/* PROGRAM: QQAPI1 */
/* */
/* LANGUAGE: ILE C FOR OS/400 */
/* */
/* DESCRIPTION: THIS PROGRAM DEFINES A SIMPLE QUERY TO PERFORM */
/* ORDERING. */
/* */
/* APIs USED: QQQQRY */
/* */
/********************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <recio.h>
#include <qdbrtvfd.h>
#include <qqqqry.h>
#include "qqapi.h"
/* Query variables */
QDBUFCB_T ufcbBuf;
char qdtBuf[QDT_SIZE];
char formatBuf[FORMAT_SIZE];
QDBQH_T *qdtPtr;
Qdb_Qddfmt_t *formatPtr;
QDBQFHDR_T fileHdr;
QDBQN_T fileSpec[MAX_FILES];
QDBQKH_T orderByHdr;
QDBQKF_T orderByFld[MAX_ORDERBY];
int formatSize;
int fileSpecSize;
int orderBySize;
error_code errcod;
errcod.bytes_provided = 512;
/* initialize the pointers */
qdtPtr = (QDBQH_T *) qdtBuf;
formatPtr = (Qdb_Qddfmt_t *) formatBuf;
/********************************************************************/
/* PROGRAM: QQAPI7 */
/* */
/* LANGUAGE: ILE C FOR OS/400 */
/* */
/* DESCRIPTION: THIS PROGRAM DEFINES A JOIN QUERY. */
/* */
/* APIs USED: QQQQRY */
/* */
/********************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <recio.h>
#include <qdbrtvfd.h>
#include <qqqqry.h>
#include "qqapi.h"
/* Query variables */
QDBUFCB_T ufcbBuf;
char qdtBuf[QDT_SIZE];
char formatBuf[FORMAT_SIZE];
char selectBuf[SELECT_SIZE];
QDBQH_T *qdtPtr;
Qdb_Qddfmt_t *formatPtr;
QDBQS_T *selectPtr;
QDBQFHDR_T fileHdr;
QDBQN_T fileSpec[MAX_FILES];
QDBQJHDR_T joinHdr;
QDBQJFLD_T joinSpec[MAX_JOINTESTS];
int formatSize;
int fileSpecSize;
int selectSize;
int joinSize;
error_code errcod;
errcod.bytes_provided = 512;
/* initialize the pointers */
qdtPtr = (QDBQH_T *) qdtBuf;
formatPtr = (Qdb_Qddfmt_t *) formatBuf;
selectPtr = (QDBQS_T *) selectBuf;
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <qdbrtvfd.h>
#include <qqqqry.h>
#include "qqapi.h">
/* Query variables */
QDBUFCB_T ufcbBuf;
char qdtBuf[QDT_SIZE];
char formatBuf[FORMAT_SIZE];
char tempFormatBuf[FORMAT_SIZE];
char selectBuf[SELECT_SIZE];
QDBQH_T *qdtPtr;
Qdb_Qddfmt_t *formatPtr;
Qdb_Qddfmt_t *tempFormatPtr;
QDBQS_T *selectPtr;
QDBQFHDR_T fileHdr;
QDBQN_T fileSpec[MAX_FILES];
QDBQJHDR_T joinHdr;
QDBQJFLD_T joinSpec[MAX_JOINTESTS];
QDBQKH_T orderByHdr;
QDBQGH_T groupByHdr;
QDBQKF_T orderByFld[MAX_ORDERBY];
QDBQGF_T groupByFld[MAX_GROUPBY];
int formatSize;
int fileSpecSize;
int orderBySize;
int groupBySize;
int selectSize;
int joinSize;
error_code errcod;
H
D***************************************************************
D***************************************************************
D*
D* Program Name: ALERTS
D*
D* Programming Language: ILE RPG for OS/400
D*
D* Description: This program uses alert APIs. First, it
D* calls the Generate Alert (QALGENA) API to
D* generate an alert without sending a message
D* to QSYSOPR or QHST message queue. Then it
D* uses the Send Alert (QALSNDA) API to send
D* the alert to the OS/400 alert manager.
D*
D* Header Files Included: QUSEC - Error Code Parameter
D*
D***************************************************************
D***************************************************************
D*
D* Error Code parameter include
D*
D/COPY QSYSINC/QRPGLESRC,QUSEC
D*
D*
D* Miscellaneous data structure
D*
DRCVVAR S 512
DRCVLEN S 9B 0 INZ(%SIZE(RCVVAR))
DALERT_SIZE S 9B 0
DMSG_FILE S 20 INZ('QCPFMSG QSYS')
DMSG_ID S 7 INZ('CPA2601')
DMSG_DATA S 100
DMSG_SIZE S 9B 0 INZ(0)
DALERT_TYPE S 1 INZ('L')
DORIGIN S 10 INZ('ALERTS')
C*
C* Beginning of mainline
C*
C* Set error handling
C*
C EVAL QUSBPRV = %SIZE(QUSEC)
C*
C* Start by generating an alert for a specific message
C*
C CALL 'QALGENA'
C PARM RCVVAR
C PARM RCVLEN
C PARM ALERT_SIZE
C PARM MSG_FILE
C PARM MSG_ID
C PARM MSG_DATA
C PARM MSG_SIZE
C PARM QUSEC
C*
C* If no error reported, send the generated alert
C*
C QUSBAVL IFEQ 0
C CALL 'QALSNDA'
C PARM RCVVAR
C PARM ALERT_SIZE
C PARM ALERT_TYPE
C PARM ORIGIN
C PARM QUSEC
C*
C* If error on send, then display the error message
C*
C QUSBAVL IFNE 0
C QUSEI DSPLY
C END
C*
C* If error on generation, then display the error message
C*
C ELSE
C QUSEI DSPLY
C END
C*
C EVAL *INLR = '1'
C RETURN
C*
C* End of MAINLINE
C*
Diagnostic Reporting
The following example program illustrates the use of the Send Nonprogram Message API, QMHSNDM, the Receive Program
Message API, QMHRCVPM, and the Change Exception Message API, QMHCHGEM. The program produces a diagnostic report of
errors that occur when the QMHSNDM API is used to send a message to more than one message queue.
The program calls the QMHSNDM API to send a message to message queues that do not exist. The QMHSNDM API returns a
generic exception message, CPF2469. This message indicates that the API also returned one or more diagnostic messages describing
the errors. After the program receives the exception message and verifies that it is message CPF2469, it uses the QMHCHGEM API
to handle the exception message. The QMHRCVPM API is used to receive the diagnostic messages. The program prints the
exception message, the diagnostic messages, and the message help.
/********************************************************************/
/* */
/* MODULE NAME: DIAGRPT - Diagnostic Report */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* FUNCTION: This module will produce a diagnostic report that */
/* could be used in diagnosing the errors that */
/* occurred using the QMHSNDM API to send a message */
/* to multiple message queues. */
/* */
/* This program purposely causes the QMHSNDM API to */
/* try to send a message to message queues that do */
/* not exist. As a result, the generic CPF2469 */
/* exception is returned indicating that one or more */
/* diagnostic messages were returned identifying the */
/* error(s) on the send operation. */
/* */
/* The program looks for and handles the CPF2469 */
/* exception. It then receives and prints out the */
/* exception and the previous diagnostics. */
/* */
/* Dependency: A print file must be created before calling */
/* program DIAGRPT. The print file should be created */
/* using the following command: */
/* */
/* CRTPRTF FILE(PRTDIAG) CTLCHAR(*FCFC) */
/* CHLVAL((1 (13))) */
/********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <except.h>
#include <qmhchgem.h> /* From QSYSINC/H */
#include <qmhrcvpm.h> /* From QSYSINC/H */
#include <qmhsndm.h> /* From QSYSINC/H */
#include <qusec.h> /* From QSYSINC/H */
/*********************************************************************/
/* Type definition for error code structure */
/*********************************************************************/
typedef struct error_code_struct
{
Qus_EC_t ec_fields;
char Exception_Data[100];
} error_code_struct;
/*********************************************************************/
/* Type definition for qualified name structure */
/*********************************************************************/
typedef struct qual_name_struct
{
char name[10];
char libr[10];
} qual_name_struct;
/*********************************************************************/
/* Type definition for message information structure used on the */
/* receive. F is the fixed portion of the record and V is the */
/* variable length portion of the record. */
/*********************************************************************/
typedef struct msg_info_struct
{
Qmh_Rcvpm_RCVM0200_t F;
char V[1200];
} msg_info_struct;
FILE *prtf;
char buf[80];
char received[7];
int exception_count;
/*********************************************************************/
/* Function to handle errors received on the API calls. */
/*********************************************************************/
static void excp_handler(_INTRPT_Hndlr_Parms_T *excp_info)
{
error_code_struct Error_Code;
if (strncmp(excp_info->Msg_Id,"CPF2469",7) == 0) {
memcpy(received,(excp_info->Msg_Id),7);
exception_count++;
QMHCHGEM(&(excp_info->Target), 0,
(char *)(&(excp_info->Msg_Ref_Key)),
"*HANDLE ", "", 0, &Error_Code);
}
}
/********************************************************************/
/* BuildQList: Routine to build the message queue list. */
/********************************************************************/
void BuildQList( qual_name_struct *QueueList, int NumQueue)
{
int i;
strncpy(QueueList[0].name,"QPGMR ",10);
strncpy(QueueList[1].name,"SNOOPY ",10);
strncpy(QueueList[2].name,"QSECOFR ",10);
strncpy(QueueList[3].name,"PEANUTS ",10);
strncpy(QueueList[4].name,"QUSER ",10);
/********************************************************************/
/* PrintError: Routine to print error information and exit. */
/********************************************************************/
void PrintError(char *errstring, char exception[7])
{
memset(buf,' ',BUF_SIZE);
buf[0] = '0';
strncpy(buf+1,errstring,strlen(errstring));
fwrite(buf,1,BUF_SIZE,prtf);
memset(buf,' ',BUF_SIZE);
buf[0] = '0';
strncpy(buf+1,"Exception received->",20);
strncpy(buf+21,exception,strlen(exception));
fwrite(buf,1,BUF_SIZE,prtf);
fclose(prtf);
exit(1);
}
/********************************************************************/
/* PrintData: Routine to print varying length character string data.*/
/********************************************************************/
void PrintData(char *strname, void *strptr, int strlgth)
{
char *strdata = strptr;
int i,lgth,remain;
/* Write the description and the data that will fit on one line */
memset(buf,' ',BUF_SIZE);
buf[0] = '0';
lgth = strlen(strname);
strncpy(buf+1,strname,lgth);
lgth++;
}
}
/********************************************************************/
/* PrintMessage: Routine to print the message data and text. */
/********************************************************************/
void PrintMessage(msg_info_struct *Msg)
{
char *DataPtr; /* Pointer to the varying length character data*/
int DataLen; /* Length of the varying length character data */
char CharType[10]; /* Message type as a string */
PrintData("Message ID->",Msg->F.Message_Id,7);
/* Convert Message Type to a character string to be printed out */
if (memcmp(Msg->F.Message_Type,"02",2)==0)
strncpy(CharType,"DIAGNOSTIC", 10);
else if (memcmp(Msg->F.Message_Type,"15",2)==0)
strncpy(CharType,"ESCAPE ",10);
PrintData("Message Type->",CharType,10);
/* Point to the beginning of the message text field and get the */
/* length of message text returned. */
DataPtr += DataLen;
DataLen = Msg->F.Length_Message_Returned;
/* If there is non-blank text, print it out */
if ((DataLen > 0) && (strspn( DataPtr," ") < DataLen))
PrintData("Message text received->",DataPtr,DataLen);
/*********************************************************************/
/* */
/* Start of main program. */
/* */
/*********************************************************************/
main()
{
error_code_struct ErrorCode;
qual_name_struct MsgQList[5];
qual_name_struct MsgFile;
qual_name_struct RpyMsgQ;
msg_info_struct MsgInfo;
char MsgData[128];
char MsgText[512];
char MsgHelp[512];
char PgmMsgQ[10];
char MsgType[10];
char MsgAction[10];
char Format[8];
char MsgId[7];
char MsgKey[4];
int MsgTextLen;
int MsgInfoLen;
int NumMsgQ;
int PgmCount;
int WaitTime;
int morediag;
/* Initialize variables */
exception_count = 0;
memcpy(ErrorCode.ec_fields.Exception_Id," ",7);
ErrorCode.ec_fields.Bytes_Provided = 0;
memcpy(MsgId," ",7);
memcpy(MsgFile.name," ",10);
memcpy(MsgFile.libr," ",10);
strcpy(MsgText,"This is an immediate, informational message");
MsgTextLen = strlen(MsgText);
memcpy(MsgType,"*INFO ",10);
memcpy(RpyMsgQ.name," ",10);
memcpy(RpyMsgQ.libr," ",10);
while(morediag)
{
/* Receive the previous diagnostic */
QMHRCVPM(&MsgInfo,
MsgInfoLen,
Format,
PgmMsgQ,
PgmCount,
MsgType,
MsgKey,
WaitTime,
MsgAction,
&ErrorCode);
/* Write trailer */
memset(buf,' ',BUF_SIZE);
strncpy(buf,"- END OF DIAGNOSTIC REPORT",48);
fwrite(buf,1,BUF_SIZE,prtf);
} /* End mainline */
Printed Diagnostic Report
The DIAGRPT program produces a report like this:
Message ID->CPF2469
Message Type->ESCAPE
Message text received->Error occurred when sending message.
Message help text received->Recovery . . . : See messages
previously listed for a description of the error.
Correct the error, and then try the
command again.
Message ID->CPF2403
Message Type->DIAGNOSTIC
Message data received->PEANUTS *LIBL
Message text received->Message queue PEANUTS in *LIBL not found.
Message help text received->Cause . . . . . : The message queue you
specified was not found in the library you specified. One
of the following occurred: -- The queue name was not
entered correctly. -- The queue does not exist in the
specified library. -- You specified the wrong library name.
Recovery . . . : Do one of the following and try the
request again: -- Correct or change the message queue
name or library name used in the message queue (MSGQ)
parameter or the to-message queue (TOMSGQ) parameter.
-- Create the message queue using the Create Message
Queue (CRTMSGQ) command.
Message ID->CPF2403
Message Type->DIAGNOSTIC
Message data received->SNOOPY *LIBL
Message text received->Message queue SNOOPY in *LIBL not found.
Message help text received->Cause . . . . . : The message queue
you specified was not found in the library you specified.
One of the following occurred: -- The queue name was not
entered correctly. -- The queue does not exist in the
specified library. -- You specified the wrong library
name. Recovery . . . : Do one of the following and
try the request again: -- Correct or change the message
queue name or library name used in the message queue
(MSGQ) parameter or the to-message queue (TOMSGQ)
parameter. -- Create the message queue using the Create
Message Queue (CRTMSGQ) command.
End of Diagnostic Report
Listing Directories
You should call this program with only one parameter, the parameter that represents the directory you want to list.
/******************************************************************/
/******************************************************************/
/* FUNCTION: This program lists a directory to a spooled file. */
/* */
/* LANGUAGE: ILE C */
/* */
/* */
/* APIs USED: QHFOPNDR, QHFRDDR, QHFCLODR, QHFLSTFS, QUSCRTUS, */
/* QUSRTVUS */
/* */
/******************************************************************/
/******************************************************************/
/******************************************************************/
/* INCLUDE FILES */
/******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <qhfopndr.h>
#include <qhfrddr.h>
#include <qhfclodr.h>
#include <qhflstfs.h>
#include <quscrtus.h>
#include <qusrtvus.h>
#include <qusec.h>
/******************************************************************/
/* STRUCTURE AND VARIABLE DECLARATIONS */
/******************************************************************/
/******************************************************************/
/* Parameters for QHFOPNDR */
/******************************************************************/
char dir_handle[16]; /* Directory handle */
int namelen; /* Length of path name */
char openinfo[6]; /* Open information */
typedef struct {
Qhf_Attr_Selec_Tbl_t fixed;
int offset2;
int offset3;
int att_len1;
char att_name1[8];
int att_len2;
char att_name2[8];
int att_len3;
char att_name3[8];
} selection_struct;
selection_struct select;
int selectionlen;
/******************************************************************/
/* Error Code Structure */
/* */
/* This shows how the user can define the variable length portion */
/* of error code for the exception data. */
/* */
/******************************************************************/
typedef struct {
Qus_EC_t ec_fields;
char Exception_Data[100];
} error_code_t;
error_code_t error_code;
/******************************************************************/
/* Parameters for QHFRDDR */
/******************************************************************/
/* The directory handle is the same as for QHFOPNDR */
typedef struct {
Qhf_Data_Buffer_t fixed;
int num_att;
int offsets[4];
char attinfo[276];
} read_buffer;
read_buffer buffer;
int result_count;
int bytes_returned;
/******************************************************************/
/* Parameters for QHFCLODR */
/******************************************************************/
/* No additional ones need to be declared */
/******************************************************************/
/* Parameters for QUSCRTUS */
/******************************************************************/
int size;
char text[50];
/******************************************************************/
/* Parameters for QHFLSTFS */
/******************************************************************/
/* No additional ones need to be declared */
/******************************************************************/
/* Parameters for QUSRTVUS */
/******************************************************************/
int startpos;
int len;
char charbin4[4];
char FSname[10];
/******************************************************************/
/* Other declarations */
/******************************************************************/
int entrypos;
int numentries;
int entrylen;
char *att;
char name[100];
char attname[30];
char attval[30];
int attnamelen;
int attvallen;
char newname[30];
int filesize;
char fileatt[10];
typedef struct {
char century;
char year[2];
char month[2];
char day[2];
char hour[2];
char minute[2];
char second[2];
} charval;
charval chartime;
int bytes_used;
int i;
error_code.ec_fields.Bytes_Provided = 0;
/****************************************************************/
/* Make sure we received the correct number of parameters. The */
/* argc parameter will contain the number of parameters that */
/* was passed to this program. This number also includes the */
/* program itself, so we need to evaluate argc-1. */
/****************************************************************/
/****************************************************************/
/* Open QPRINT file so that data can be written to it. If the */
/* file cannot be opened, print a message and exit. */
/****************************************************************/
if((stream = fopen("QPRINT", "wb")) == NULL)
{
printf("File could not be opened\n");
exit(1);
}
/****************************************************************/
/* List the file systems into that space. */
/****************************************************************/
QHFLSTFS("FSLST QTEMP ", "HFSL0100", &error_code);
/****************************************************************/
/* Get the starting point for the file system entries. */
/****************************************************************/
startpos = 125;
len = 4;
QUSRTVUS("FSLST QTEMP ", startpos, len, charbin4,
&error_code);
/****************************************************************/
/* Get the number of entries in the user space. */
/****************************************************************/
startpos = 133;
len = 4;
/****************************************************************/
/* Find the length of the entries. */
/****************************************************************/
startpos = 137;
len = 4;
/****************************************************************/
/* Loop through the entries and get the names of the file */
/* systems. */
/****************************************************************/
for(i=0;i<numentries;++i)
{
startpos = entrypos + 1;
len = 10;
QUSRTVUS("FSLST QTEMP ", startpos, len, FSname,
&error_code);
/*************************************************************/
/* List the names into the spooled file. */
/*************************************************************/
sprintf(write_string," %.10s <DIR>", FSname);
fprintf(stream, write_string);
entrypos = entrypos + entrylen;
}
}
else
{
fprintf(stream,"Directory listing for path %.100s\n", name);
/****************************************************************/
/* Build the attribute selection table for QHFOPNDR. */
/****************************************************************/
select.fixed.Number_Attributes = 3;
select.fixed.Offset_First_Attr = 16;
select.offset2 = 28;
select.offset3 = 40;
select.att_len1 = 8;
memcpy(select.att_name1, "QFILSIZE", 8);
select.att_len2 = 8;
memcpy(select.att_name2, "QCRTDTTM", 8);
select.att_len3 = 8;
memcpy(select.att_name3, "QFILATTR", 8);
selectionlen = 52;
memcpy(openinfo, "10 ", 6);
/****************************************************************/
/* Find the length of the directory name. */
/****************************************************************/
for(i=0;i<100;i++)
{
if((name[i] == ' ') || (name[i] == '\x00'))
break;
}
namelen = i;
/****************************************************************/
/* Open the directory. */
/****************************************************************/
QHFOPNDR(dir_handle, name, namelen, openinfo, &select, selectionlen,
&error_code);
/****************************************************************/
/* Read one entry from the directory. */
/****************************************************************/
QHFRDDR(dir_handle, &buffer, 300, 1, &result_count, &bytes_returned,
&error_code);
while(result_count > 0)
{
memcpy(attname," ",30);
memcpy(attval," ",30);
att = buffer.attinfo;
bytes_used = 20;
/**************************************************************/
/* Loop for the number of attributes in the entry. */
/**************************************************************/
for(i=0;i<buffer.num_att;i++)
{
memcpy(charbin4, att, 4);
attnamelen = *(int *)charbin4;
att += 4;
bytes_used += 4;
memcpy(charbin4, att, 4);
attvallen = *(int *)charbin4;
att += 8;
bytes_used += 8;
memcpy(attname, att, attnamelen);
att += attnamelen;
bytes_used += attnamelen;
memcpy(attval, att, attvallen);
att += attvallen;
bytes_used += attvallen;
/************************************************************/
/* Update att so that its first character is the first */
/* character of the next attribute entry. */
/************************************************************/
if ((bytes_used == buffer.offsets[i+1]) &&
((i+1) == buffer.num_att))
att += (buffer.offsets[i] - bytes_used);
/************************************************************/
/* If the attribute is QNAME, then set newname. */
/************************************************************/
if(!memcmp(attname, "QNAME", 5))
{
memset(newname, ' ', 12);
memcpy(newname, attval, attvallen);
}
/************************************************************/
/* If the attribute is QFILSIZE, then set filesize. */
/************************************************************/
else if(!memcmp(attname, "QFILSIZE", 8))
{
memcpy(charbin4, attval, 4);
filesize = *(int *)charbin4;
}
/************************************************************/
/* If it was QCRTDTTM, then set the time. */
/************************************************************/
else if(!memcmp(attname, "QCRTDTTM", 8))
memcpy(&chartime, attval, 13);
/************************************************************/
/* Else the attribute was QFILATTR, so set fileatt. */
/************************************************************/
else
memcpy(fileatt, attval, 10);
}
/**************************************************************/
/* If the entry was a directory, list its name and <DIR>. */
/**************************************************************/
if(fileatt[3] == '1')
{
sprintf(write_string," %s <DIR>", newname);
fprintf(stream, write_string);
}
/**************************************************************/
/* If the entry is not a hidden file, list its name and size. */
/**************************************************************/
else if(fileatt[1] == '0')
{
sprintf(write_string," %s %d", newname, filesize);
fprintf(stream, write_string);
}
/**************************************************************/
/* If the entry is not a hidden file or directory, list its */
/* date of creation. */
/**************************************************************/
if(fileatt[1] == '0')
{
sprintf(write_string," %.2s-%.2s-%.2s", chartime.month,
chartime.day, chartime.year);
fprintf(stream, write_string);
sprintf(write_string," %.2s:%.2s:%.2s\n", chartime.hour,
chartime.minute, chartime.second);
fprintf(stream, write_string);
}
} /* while */
} /* else */
/****************************************************************/
/* Close the directory. */
/****************************************************************/
QHFCLODR(dir_handle, &error_code);
fclose(stream);
} /* main */
Listing Subdirectories
You should call this program with only one parameter, the parameter that represents the directory you want to list.
/******************************************************************/
/******************************************************************/
/* FUNCTION: List the subdirectories of the path passed to the */
/* program to a spooled file. */
/* */
/* LANGUAGE: ILE C */
/* */
/* */
/* APIs USED: QHFOPNDR, QHFRDDR, QHFCLODR */
/* */
/******************************************************************/
/******************************************************************/
/******************************************************************/
/* INCLUDE FILES */
/******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <qhfopndr.h>
#include <qhfrddr.h>
#include <qhfclodr.h>
#include <qusec.h>
char write_string[100];
FILE *stream;
/******************************************************************/
/* Parameters for QHFOPNDR */
/******************************************************************/
char dir_handle[16]; /* Directory handle */
int namelen; /* Length of path name */
char openinfo[6]; /* Open information */
typedef struct {
Qhf_Attr_Selec_Tbl_t fixed;
int att_len;
char att_name[8];
} selection_struct;
selection_struct select;
int selectionlen;
/******************************************************************/
/* Error Code Structure */
/* */
/* This shows how the user can define the variable length */
/* portion of error code for the exception data. */
/* */
/******************************************************************/
typedef struct {
Qus_EC_t ec_fields;
char Exception_Data[100];
} error_code_t;
error_code_t error_code;
/******************************************************************/
/* Parameters for QHFRDDR */
/******************************************************************/
/* The directory handle is the same as for QHFOPNDR */
typedef struct {
Qhf_Data_Buffer_t fixed;
int num_att;
int offsets[2];
char attinfo[180];
} read_buffer;
read_buffer buffer;
int result_count;
int bytes_returned;
/******************************************************************/
/* Parameters for QHFCLODR */
/******************************************************************/
/* No additional ones need to be declared */
/******************************************************************/
/* Other declarations */
/******************************************************************/
char *att;
char attname[30];
char attval[30];
int attnamelen;
int attvallen;
char newname[30];
int newnamelen;
int filesize;
char fileatt[10];
char tab[5];
int bytes_used;
int i, j;
char charbin4[4];
char tempname[100];
error_code.ec_fields.Bytes_Provided = 0;
/****************************************************************/
/* Build the attribute selection table for QHFOPNDR. */
/****************************************************************/
select.fixed.Number_Attributes = 1;
select.fixed.Offset_First_Attr = 8;
select.att_len = 8;
memcpy(select.att_name, "QFILATTR", 8);
selectionlen = 20;
memcpy(openinfo, "10 ", 6);
memcpy(tab," ", 5);
/****************************************************************/
/* Find the length of the directory name. */
/****************************************************************/
for(i=0;i<100;i++)
{
if((name[i] == ' ') || (name[i] == '\x00'))
break;
}
namelen = i;
/****************************************************************/
/* Open the directory. */
/****************************************************************/
QHFOPNDR(dir_handle, name, namelen, openinfo, &select, selectionlen,
&error_code);
/****************************************************************/
/* Read one entry from the directory. */
/****************************************************************/
QHFRDDR(dir_handle, &buffer, 200, 1, &result_count, &bytes_returned,
&error_code);
fprintf(stream,"\n");
for(i=0;i<numtabs;i++)
fprintf(stream, tab);
fprintf(stream, name);
while(result_count > 0)
{
memcpy(attname," ",30);
memcpy(attval," ",30);
att = buffer.attinfo;
bytes_used = 12;
/**************************************************************/
/* Loop for the number of attributes in the entry. */
/**************************************************************/
for(i=0;i<buffer.num_att;i++)
{
memcpy(charbin4, att, 4);
attnamelen = *(int *)charbin4;
att += 4;
bytes_used += 4;
memcpy(charbin4, att, 4);
attvallen = *(int *)charbin4;
att += 8;
bytes_used += 8;
memcpy(attname, att, attnamelen);
att += attnamelen;
bytes_used += attnamelen;
memcpy(attval, att, attvallen);
att += attvallen;
bytes_used += attvallen;
/************************************************************/
/* Update att so that its first character is the first */
/* character of the next attribute entry. */
/************************************************************/
if ((bytes_used == buffer.offsets[i+1]) &&
((i+1) == buffer.num_att))
att += (buffer.offsets[i] - bytes_used);
/************************************************************/
/* If the attribute is QNAME, then set newname and */
/* newnamelen just in case the entry is a directory. */
/************************************************************/
if(!memcmp(attname, "QNAME", 5))
{
memcpy(newname, attval, attvallen);
newnamelen = attvallen;
}
/************************************************************/
/* Else the attribute was QFILATTR, so set fileatt. */
/************************************************************/
else
memcpy(fileatt, attval, 10);
}
/**************************************************************/
/* If the entry was a directory, construct new path name and */
/* print_subdir to print the subdirectory. */
/**************************************************************/
if(fileatt[3] == '1')
{
memcpy(tempname, name, 100);
strcat(name, "/");
strcat(name, newname);
memcpy(newname, name, namelen + newnamelen + 1);
print_subdir(newname, numtabs + 1);
memcpy(name, tempname, 100);
}
} /* while */
/****************************************************************/
/* Close the directory. */
/****************************************************************/
QHFCLODR(dir_handle, &error_code);
}/* print_subdir */
/****************************************************************/
/* Make sure we received the correct number of parameters. The */
/* argc parameter will contain the number of parameters that */
/* was passed to this program. This number also includes the */
/* program itself, so we need to evaluate argc-1. */
/****************************************************************/
/****************************************************************/
/* Open QPRINT file so that data can be written to it. If the */
/* file cannot be opened, print a message and exit. */
/****************************************************************/
if((stream = fopen("QPRINT", "wb")) == NULL)
{
printf("File could not be opened\n");
exit(1);
}
fclose(stream);
} /* main */
/*******************************************************************/
/* PROGRAM: SaveBigLib */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* DESCRIPTION: This is an example program for the use of */
/* a media definition in a save operation. */
/* It saves library BIGLIB in parallel format to */
/* two media files, using tape media library */
/* TAPMLB01. */
/* */
/* The flow of this program is as follows: */
/* (1) Build media definition input. */
/* (2) Create a media definition using */
/* QsrCreateMediaDefinition. */
/* (3) Save library BIGLIB using the media */
/* definition. */
/* */
/* APIs USED: QsrCreateMediaDefinition, QCMDEXC */
/* */
/*******************************************************************/
#include <qcmdexc.h>
#include <qsrlib01.h>
#include <qusec.h>
#include <string.h>
/*******************************************************************/
/* Variables for QsrCreateMediaDefinition */
/*******************************************************************/
char Data_Buffer[1000];
Qsr_TAPE0100_t *Input_Data;
Qsr_TAPE0100_Device_t *Device;
Qsr_TAPE0100_File_t *Media_File;
char *Next_Free;
char *Volid;
Qus_EC_t Err_Code;
int Data_Length;
char Text[50];
/*******************************************************************/
/* Variables for QCMDEXC */
/*******************************************************************/
char Cmd_String[100];
decimal(15,5) Cmd_Length;
/*******************************************************************/
/* Start of main() */
/*******************************************************************/
/*******************************************************************/
/* Specify input data for QsrCreateMediaDefinition. */
/*******************************************************************/
/*-----------------------------------------------------------------*/
/* Build general media definition input data. */
/* Use one device with two parallel device resources. */
/*-----------------------------------------------------------------*/
memset(Data_Buffer,0,sizeof(Data_Buffer));
Input_Data = (Qsr_TAPE0100_t*)Data_Buffer;
Next_Free = (char*)(Input_Data + 1);
Input_Data->Maximum_Resources = 2;
Input_Data->Minimum_Resources = 2;
Input_Data->Offset_First_Device = Next_Free - Data_Buffer;
Input_Data->Device_Count = 1;
/*-----------------------------------------------------------------*/
/* Build input data for the first device. */
/* Use device TAPMLB01 with two media files. */
/*-----------------------------------------------------------------*/
Device = (Qsr_TAPE0100_Device_t*)Next_Free;
Next_Free = (char*)(Device + 1);
memcpy(Device->Device_Name,"TAPMLB01 ",10);
Device->Offset_First_File = Next_Free - Data_Buffer;
Device->File_Count = 2;
/*-----------------------------------------------------------------*/
/* Build input data for the first media file for device TAPMLB01. */
/* Use the default sequence number, and volumes VOL11 and VOL12. */
/*-----------------------------------------------------------------*/
Media_File = (Qsr_TAPE0100_File_t*)Next_Free;
Next_Free = (char*)(Media_File + 1);
Media_File->Sequence_Number = 0;
Media_File->Offset_First_Volume_Id = Next_Free - Data_Buffer;
Media_File->Volume_Id_Count = 2;
Media_File->Volume_Id_Length = 6;
Media_File->Starting_Volume = 1;
Data_Length = Media_File->Volume_Id_Count
* Media_File->Volume_Id_Length;
Volid = Next_Free;
memcpy(Volid,"VOL11 VOL12 ",Data_Length);
if (Data_Length % 4) /* Ensure that Next_Free */
Data_Length += (4 - (Data_Length % 4)); /* is incremented by a */
Next_Free += Data_Length; /* multiple of 4. */
Media_File->Offset_Next_File = Next_Free - Data_Buffer;
/*------------------------------------------------------------------*/
/* Build input data for the second media file for device TAPMLB01. */
/* Use the default sequence number, and volumes VOL21 and VOL22. */
/*------------------------------------------------------------------*/
Media_File = (Qsr_TAPE0100_File_t*)Next_Free;
Next_Free = (char*)(Media_File + 1);
Media_File->Sequence_Number = 0;
Media_File->Offset_First_Volume_Id = Next_Free - Data_Buffer;
Media_File->Volume_Id_Count = 2;
Media_File->Volume_Id_Length = 6;
Media_File->Starting_Volume = 1;
Data_Length = Media_File->Volume_Id_Count
* Media_File->Volume_Id_Length;
Volid = Next_Free;
memcpy(Volid,"VOL21 VOL22 ",Data_Length);
if (Data_Length % 4) /* Ensure that Next_Free */
Data_Length += (4 - (Data_Length % 4)); /* is incremented by a */
Next_Free += Data_Length; /* multiple of 4. */
/********************************************************************/
/* Create the media definition. */
/********************************************************************/
Data_Length = Next_Free - Data_Buffer;
memset(Text,' ',sizeof(Text));
memcpy(Text,"Save BIGLIB",11);
QsrCreateMediaDefinition(
"SAVEBIGLIBQTEMP ", /* Media definition */
name, library */
Data_Buffer, /* Input data */
Data_Length, /* Length of data */
"TAPE0100", /* Format name */
"*USE ", /* Public authority */
Text, /* Text description */
1, /* Replace if it exists */
&Err_Code); /* Error code */
/********************************************************************/
/* Save library BIGLIB using the media definition. */
/********************************************************************/
strcpy(Cmd_String,
"SAVLIB LIB(BIGLIB) DEV(*MEDDFN) MEDDFN(QTEMP/SAVEBIGLIB)");
Cmd_Length = strlen(Cmd_String);
QCMDEXC(Cmd_String,Cmd_Length);
return 0;
}
Example 1
Assume a 20-character database field containing only uppercase characters and the pattern 'ABC' is scanned for. The user program
calls the QCLSCAN API for each database record read. The parameters would be as follows:
Field Name Result
STRING The 20-byte field to be scanned
STRLEN 20
STRPOS 1
PATTERN 'ABC'
PATLEN 3
TRANSLATE '0'
TRIM '0'
WILD ''
RESULT A value returned to your program
The following describes some fields and the results of the scan:
Note: In scan 4, the string has two places where the pattern could have been found. Since the STRPOS value is 1, the first value
(position 004) was found. If the value of STRPOS had been 4, the result would still have been 004. If the STRPOS value had been in
a range of 5 through 12, the result would have been 012.
Example 2
Assume a 25-character database field containing only uppercase characters and a user program that will prompt for the pattern to be
scanned, which will not exceed 10 characters. The work station user is allowed to enter 1 through 10 characters to search with and
trailing blanks will be trimmed from the pattern. The program would call the QCLSCAN program for each database record read. The
program parameters would be as follows:
Field Name Result
STRING The 25-byte field to be scanned
STRLEN 25
STRPOS 1
PATTERN Varies
PATLEN 10
TRANSLATE '0'
TRIM '1'
WILD ''
RESULT A value returned to your program
The following describes some fields and the results of the scan:
Note: In scan 7, the string has two places where the pattern could be found. Since the STRPOS value is 1, only the first value
(position 004) is found. If the value of STRPOS were 4, the result would still be 004. If the STRPOS value were in the range of 5
through 12, the result would be 012.
Example 3
Assume a 25-character database field containing either uppercase or lowercase characters. The user program prompts for the pattern
to be scanned, which does not exceed 5 characters. The work station user can enter 1 through 5 characters to be found. The system
trims trailing blanks from the pattern. If the user enters an asterisk (*) in the pattern, the asterisk is handled as a wild character. The
program calls the QCLSCAN program for each database record read. The parameters are as follows:
Field Name Result
STRING The 25-byte field to be scanned
STRLEN 25
STRPOS 1
PATTERN Varies
PATLEN 5
TRANSLATE '1' (See note 1)
TRIM '1'
WILD '*'
RESULT A value returned to your program
The following describes some fields and the results of the scan:
Notes:
1. When field translation is specified (the TRANSLATE parameter is specified as '1'), the string is translated to uppercase
characters before scanning occurs; the data in the string is not changed.
2. In scan 6, the string has two places where the pattern could have been found. Since the STRPOS value is 1, the first value
(position 004) was found.
3. In scan 7, the wild character (*) is the first character in the trimmed pattern. Wild characters cannot be the first character in a
pattern.
4. In scan 8, the trimmed pattern is blank.
Note: In order for this example to run successfully, the error program, ACERRF24 (shown in Error Handler for Example COBOL
Program), must exist in a library called UTCBL.
IDENTIFICATION DIVISION.
PROGRAM-ID. ACF24.
**************************************************************
**************************************************************
*
* FUNCTION: SHOWS HOW TO CALL THE VARIOUS APIs, WHILE
* TESTING THAT THEY WORK PROPERLY.
*
* LANGUAGE: COBOL FOR OS/400
*
* APIs USED: QLRRTVCE, QLRCHGCM, QLRSETCE
*
**************************************************************
**************************************************************
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-AS400.
OBJECT-COMPUTER. IBM-AS400.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 old.
05 oldname PIC X(10).
05 oldlibr PIC X(10).
77 scope PIC X VALUE "P".
01 errparm.
05 input-l PIC S9(6) BINARY VALUE ZERO.
05 output-l PIC S9(6) BINARY VALUE ZERO.
05 exception-id PIC X(7).
05 reserved PIC X(1).
05 exception-data PIC X(50).
01 new.
05 newname PIC X(10) VALUE "ACERRF24".
05 newlibr PIC X(10) VALUE "UTCBL".
77 newlib PIC X(10).
PROCEDURE DIVISION.
main-proc.
DISPLAY "in ACF24".
PERFORM variation-01 THRU end-variation.
STOP RUN.
variation-01.
**************************************************************
* *
* This variation addresses the situation where there is no *
* pending COBOL main, so no pending error handler can exist. *
* *
**************************************************************
DISPLAY "no pending so expect nothing but error LBE7052".
MOVE SPACES TO old exception-id.
**************************************************************
* By setting error parm > 8, expect escape message *
* LBE7052 to be returned in error parameter. *
**************************************************************
MOVE LENGTH OF errparm TO input-l.
CALL "QLRRTVCE" USING old scope errparm.
IF exception-id IS NOT = "LBE7052" THEN
DISPLAY "** error - expected LBE7052"
ELSE
DISPLAY "LBE7052 was found"
END-IF.
**************************************************************
* Reset input-l to ZERO, thus any further errors will cause *
* COBOL program to stop. *
**************************************************************
MOVE 0 TO input-l.
MOVE SPACES TO old exception-id.
variation-02.
**************************************************************
* *
* This variation creates a pending run unit. It then makes *
* sure that no pending error handler has been set. *
* *
**************************************************************
DISPLAY "create pending run unit".
CALL "QLRCHGCM" USING errparm.
**************************************************************
* *
* No pending error handler exists so *NONE should be *
* returned. *
* *
**************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. ACERRF24.
**************************************************************
**************************************************************
*
* FUNCTION: Error handler for preceding example COBOL program
*
* LANGUAGE: COBOL FOR OS/400
*
* APIs USED: None
*
**************************************************************
**************************************************************
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-AS400.
OBJECT-COMPUTER. IBM-AS400.
SPECIAL-NAMES. SYSTEM-CONSOLE IS SYSCON.
DATA DIVISION.
WORKING-STORAGE SECTION.
77 scope PIC X VALUE "P".
01 errparm.
05 FILLER PIC X(30).
LINKAGE SECTION.
77 cobol-id PIC X(7).
77 valid-responses PIC X(6).
01 progr.
05 progname PIC X(10).
05 proglibr PIC X(10).
77 system-id PIC X(7).
77 len-text PIC S9(9) COMP-4.
01 subtext.
03 subchars PIC X OCCURS 1 TO 230 TIMES
DEPENDING ON len-text.
77 retcode PIC X(1).
PROCEDURE DIVISION USING cobol-id, valid-responses,
progr, system-id, subtext, len-text, retcode.
main-proc.
**********************************************************
* check for typical messages and take appropriate action *
**********************************************************
EVALUATE cobol-id
WHEN "LBE7604"
**********************************************************
* stop literal, let the user see the message *
**********************************************************
MOVE SPACE TO retcode
WHEN "LBE7208"
**********************************************************
* accept/display, recoverable problem answer G to continue
**********************************************************
MOVE "G" TO retcode
WHEN OTHER
**********************************************************
* for all other messages signal system operator and *
* end the current run unit *
**********************************************************
DISPLAY "COBOL Error Handler ACERRF24 "
"Found message " cobol-id
" Issued from program " progr
UPON syscon
DISPLAY " Ended current run unit"
UPON syscon
MOVE "C" TO retcode
END-EVALUATE.
GOBACK.
X.25 Overview
In this example X.25 network, the source application on System A is responsible for establishing a switched virtual circuit, or
connection to the target application running on System B. This is done by using the remote network address (System B's address) of
X'0000652'. When the target application on System B is initialized, it waits for notification of an incoming call packet before
proceeding. Once the virtual circuit is established, the source application reads records from a file into its output buffer and sends
them to the target application using normal X.25 data transfer procedures. While receiving the file data, the target application writes
the data to a local file on System B. When the file transfer completes, the source application closes the connection by issuing an X.25
clear request packet and ends. When receiving the clear indication packet, the target application also ends.
Note: Keyed data queue support is used by both applications. The key length is 3 and the keys used are source (SRC) and target
(TGT) for the source and target applications, respectively.
Activating Filters
Once the links have been enabled and both applications have read their respective enable-complete entries from their data queues, the
target application program calls the Set Filter (QOLSETF) API to activate a filter. The filter activated then identifies the protocol of
the local X.25 service user. This filter is used by the user-defined communications support on System B to route incoming calls. The
actual filter type activated is X'00' (for X.25 PID) and its associated value is X'21'. For more information concerning filters, see .
After activating the X'21' filter, the target application waits for the source application to request a connection.
Establishing a Connection
The source application calls the Send Data (QOLSEND) API with a X'B000' operation in its output data buffer to establish a
switched virtual circuit (SVC) to the target application. Included in the first byte of the call user data is the protocol ID of the target
application, or X'21'. When the user-defined communications support on System B sees the incoming call packet with the first byte of
user data equal to a previously activated filter, the call is routed to the process responsible for activating that filter. In this case, the
target application will receive notification of an incoming call since it previously activated filter X'21'.
While waiting for the incoming call, the target application calls the Receive Data (QOLRECV) API to receive a X'B201' operation
with incoming call data. After doing so, the target application accepts the X.25 connection by calling the QOLSEND API with a
X'B400' operation in its output data buffer. See for more information.
Sending Data
Once the peer connection is established between the source and target applications running on System A and System B respectively,
the file transfer takes place. The source application reads records from a local file and calls the QOLSEND API with X'0000'
operations in its output data buffer to transfer the file data to System B. This process continues until the entire contents of the source
file has been sent to System B.
Receiving Data
After accepting the X.25 connection, the target application waits until its data queue receives incoming-data entries. When the first
entry is read from the queue, the QOLRECV API is called to determine which operation was received. Barring failure, the target
application should receive a X'0001' operation as a result of the QOLRECV API call. The data contained in the input data buffer is
the file data received from System A. While receiving the file data, the target application writes the data to a local file. This process
continues until the entire contents of the file is received from System A. The target application then assumes the file transfer is
complete when an operation other than a X'0001' operation is received after a successful call to the QOLRECV API. Most likely, the
first non-X'0001' operation received will be X'B301' operation, signalling that the user-defined communications support running on
System B received an SVC clear indication.
Once the entire contents of the file has been read and sent to System B, the source application calls the QOLSEND API with a
X'B100' operation in its output data buffer to clear the X.25 connection. Afterwards, the source application closes its local file,
disables its local link by calling the QOLDLINK API, and ends.
When the source application program sends a X'B100' operation, it causes the target application to receive a X'B301' operation. After
receiving this operation, the target application program calls the QOLSEND API with a X'B100' operation to locally close the
connection between itself and the user-defined communications support. Afterwards, the target application closes its local file,
disables its local link by calling the QOLDLINK API, and ends.
Both the source and target application programs use the user-defined communications support timer service to manage the reception
of certain operations. This is done by setting a timer before checking the data queue for an entry. For example, the target application
sets a timer to manage the reception of file data from the source application. If the timer expires, the user-defined communications
support places a timer-expired entry on the application's data queue. The target application then assumes when receiving this entry
that the source application ended abnormally. The target application can then take the appropriate action to end itself.
ILE C Compiler Listings
Below are the listings for the source and target applications described in the previous paragraphs. Note the reference numbers (for
example, (1)) in the listings. Detailed explanations of each reference number block are found in Source application program listing
references and Target application program listing references.
The target application compiler listing can be found in Target Application on System B Listing.
In this example, the source application is the initiator of all meaningful work. In summary, the source program listed on the following
pages does the following:
● Calls the QOLQLIND API to get local X.25 line information
● Calls the QOLSEND API with X'B000' operation to establish a peer (SVC) connection
● Sends the local file to the target system using X'0000' operations
● Calls the QOLSEND API with X'B100' operation to clear the peer (SVC) connection
● Calls the QOLTIMER API to manage the reception of data queue entries
To create the program using ILE C, use the Create Bound C (CRTBNDC) command.
Program name . . . . . .
:(SOURCE) . . .
Library name . . .: . . . .
UDCS_APPLS .
Source file . . . . .: . .
QCSRC . . .
Library name . . .: . . . .
UDCS_APPLS .
Source member name .: . .
SOURCE . . .
Text Description . .: . . . . .
Source Application Example
Output . . . . . . .: . .
*NONE . . .
Compiler options . .: . . . . .
*SOURCE *NOXREF *SHOWUSR
: *SHOWSYS *NOSHOWSKP *NOEXPMAC
: *NOAGR *NOPPONLY *NODEBUG
: *GEN *NOSECLVL *PRINT *LOGMSG
: *USRINCPATH
Checkout Options . . . . . . . : *NOAGR
Optimization . . . . . . . . . : *NONE
Inline Options:
Inliner . . . . . . . . . . . : *OFF
Mode . . . . . . . . . . . . : *NOAUTO
Threshold . . . . . . . . . . : 250
Limit . . . . . . . . . . . . : 2000
Debugging View . . . . . . . . : *NONE
Define Names . . . . . . . . . : *NONE
Language Level . . . . . . . . : *SOURCE
Source Margins:
Left margin . . . . . . . . . : 1
Right margin . . . . . . . . : 32754
Sequence columns:
Left column . . . . . . . . . : *NONE
Right column . . . . . . . . :
Message flagging level . . . . : 0
Compiler messages:
Message limit . . . . . . . . : *NOMAX
Message limit severity . . . : 30
Replace Program Object . . . . : *YES
User Profile . . . . . . . . . : *USER
Authority . . . . . . . . . . . : *LIBCRTAUT
Target Release . . . . . . . . : *CURRENT
System includes . . . . . . . . : *YES
/*******************************************************************/
/** Program Name: Source Application Program Example **/
/** **/
/** **/
/** Function: **/
/** This is the source application program example that uses **/
/** X.25 services provided by the user-defined communications **/
/** support to transfer a simple file to the target application **/
/** program running on system B. This program performs the **/
/** following: **/
/** 01. Open the source file name INFILE. **/
/** 02. Call QOLQLIND API to obtain local line information. **/
/** 03. Enable a link. **/
/** 04. Send a 'B000'X operation (call request). **/
/** 05. Receive a 'B001'X operation (call confirmation). **/
/** 06. Read record(s) from the file opened in step 1). and **/
/** send '0001'X operation(s) to transfer the file to **/
/** the target application program. **/
/** 07. Send a 'B100'X operation (clear call request). **/
/** 08. Receive a 'B101'X operation. **/
/** 09. Disable the link enabled in step 3). **/
/** **/
/** A data queue will be actively used to manage the operation **/
/** of this program. Data queue support will be used to monitor **/
/** for the completion of the enable and disable routines, as **/
/** well as timer expirations and incoming data. Timers are **/
/** used to ensure that there will never be an infinite wait on **/
/** the data queue. If a timer expires, the link enabled will **/
/** be disabled and the program will stop. **/
/** **/
/** Inputs: **/
/** The program expects the following input parameters **/
/** Line Name: This is the name of the line description **/
/** that will be used to call the QOLELINK API. **/
/** The line must be an X.25 line with at least **/
/** one SVC of type *SVCBOTH or *SVCOUT. **/
/** **/
/** CommHandle: This is the logical name that will be used **/
/** to identify the link enabled. **/
/** **/
/** Remote DTE Address: The is the Local Network Address **/
/** of System B. **/
/** **/
/** **/
/** Outputs: **/
/** Current status of the file transfer will be provided when **/
/** running this program. If an error should occur, then a **/
/** message will be displayed indicating where the error occurred **/
/** and the program will end. If the program completes **/
/** successfully, a "successful completion" message will be **/
/** posted. **/
/** **/
/** Language: ILE C for OS/400 **/
/** **/
/** APIs used: QOLELINK, QUSPTRUS, QOLRECV, QOLSEND, QOLDLINK, **/
/** QOLTIMER, QRCVDTAQ **/
/** **/
/*******************************************************************/
/*******************************************************************/
/*******************************************************************/
#include "header"
#include "typedef"
#include "hexconv"
(1)
void senddata(sendparms *a, char *b, desc *c, char *d, char *e, int f);
void sndformat1(sendparms *a,char *b, char *c, char *d, qlindparms *f);
void sndformat2 (sendparms *a, char *b, char *c);
void setfilters (hdrparms *a);
void byte (char *a, int b, char *c, int d);
void printespec (espec *a);
void settimer(unsigned short *a,char *b,qentry *c,usrspace *d,char *e);
void dequeue (int a, char *b, qentry *c, usrspace *d);
void x25lind (qlindparms *a, char *b);
int getline (char *a, int b, FILE *c);
void disablelink (disableparms *a, char *b, usrspace *c);
void handler (disableparms a, usrspace *b);
(3)
signal(SIGALL,SIG_DFL);
/** Clear the command line Parameters **/
strncpy(enable.linename, " ", 10); /* Clear linename */
strncpy(commhandle, " ", 10); /* Clear Commhandle*/
strncpy(rmtdte, " ", 17); /* Clear Remote DTE*/
/** Receive command line Parameters **/
strncpy(enable.linename, argv[1], strlen(argv[1]));
strncpy(commhandle, argv[2], strlen(argv[2]));
strncpy(rmtdte, argv[3], strlen(argv[3]));
rmtdte[strlen(argv[3])] = '\0';
/** Initialize the user spaces **/
strncpy(inbuff.library, "UDCS_APPLS", 10); /* Input Buffer */
strncpy(inbuff.name, "SOURCEIBUF", 10);
strncpy(indesc.library, "UDCS_APPLS", 10); /* Input B Desc */
strncpy(indesc.name, "SOURCEBDSC", 10);
strncpy(outbuff.library, "UDCS_APPLS", 10); /* Output Buffer*/
strncpy(outbuff.name, "SOURCEOBUF", 10);
strncpy(outdesc.library, "UDCS_APPLS", 10); /* Output B Desc */
strncpy(outdesc.name, "SOURCEODSC", 10);
strncpy(qname.library, "UDCS_APPLS", 10); /* Data queue */
strncpy(qname.name, "X25DTAQ ", 10);
/***** retrieve the line description information ******/
x25lind (&qlind, enable.linename);
if ((qlind.retcode != 0) || (qlind.reason != 0))
{
printf("Query line description failed.\n");
printf("Return code = %d\n", qlind.retcode);
printf("Reason code = %d\n\n", qlind.reason);
return;
}
/***** Hard Code the QOLELINK Input Parameters ******/
enable.maxdtax25 = 512;
enable.keylength = 3;
strncpy (enable.keyvalue, "SND", 3);
(4)
/**************************************************/
/************ Enable the line *******************/
/**************************************************/
QOLELINK (&(enable.retcode), &(enable.reason), &(enable.tdusize),\
&(enable.numunits), &(enable.maxdtalan), &(enable.maxdtax25),\
(char *)&inbuff, (char *)&indesc, (char *)&outbuff,\
(char *)&outdesc, &(enable.keylength), enable.keyvalue,\
(char *)&qname, enable.linename, commhandle);
if ((enable.retcode != 0) || (enable.reason != 0))
{
printf("Line %.10s with Commhandle %.10s was NOT ENABLED.\n",\
enable.linename, commhandle);
printf("Return code = %d\n", enable.retcode);
printf("Reason code = %d\n\n", enable.reason);
return;
}
(5)
/******************************************************************/
/************** Set up a Call Request Packet *******************/
/******************************************************************/
/**** Get pointers to the user spaces. ******/
QUSPTRUS(&outbuff, &buffer);
QUSPTRUS(&outdesc, &descriptor);
send.ucep = 26; /* set the UCEP number */
send.operation = 0xB000; /* send a call request */
send.numdtaelmnts = 1; /* send one data unit */
/**----------- Send the packet ---------**/
sndformat1 (&send, buffer, rmtdte, commhandle, &qlind);
if ((send.retcode != 0) || (send.reason != 0))
{
printf("Call request packet not sent\n");
printf("Return code = %d\n", send.retcode);
printf("Reason code = %d\n", send.reason);
printf("new pcep %d\n", send.newpcep);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
(7)
/*****************************************************************/
/*********** Receive the Call CONFIRMATION packet ********/
/*****************************************************************/
/*-------- Set a timer to receive a message ---------**/
expctid = 0xF0F3;
settimer(&expctid, "Rcv Call", &dataq, &qname, commhandle);
if (expctid != 0xF0F3)
{
disablelink (&disable, commhandle, &qname);
return;
}
QOLRECV (&(recv.retcode), &(recv.reason), &(recv.ucep),\
&(recv.pcep), &(recv.operation), &(recv.numdtaunits),\
&(recv.dataavail), &(recv.errorspecific), commhandle);
if ((recv.retcode != 0) || (recv.reason != 0))
{
printf("Recv Call reqst resp failed\n");
printf("return code %d\n", recv.retcode);
printf("reason code %d\n", recv.reason);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
/* Interpret the Received Operation */
if (recv.operation != 0xB001)
{
printf("Recvd opr %x instead of opr B001\n", recv.operation);
disablelink (&disable, commhandle, &qname);
return;
}
printf("We have an X.25 SVC connection\n\n");
(8)
/*****************************************************************/
/*************** Send the file to the target application *******/
/*****************************************************************/
send.pcep = send.newpcep; /* set the PCEP number */
/*************** Send the Mbr LGRF in file DOC ****************/
linesiz = getline(line, 92, fptr); /* Get first record **/
while (linesiz != 0)
{
/*************** Send a Packet of Data ***************/
/**** Get pointers to the user spaces. ******/
QUSPTRUS(&outbuff, &buffer);
QUSPTRUS(&outdesc, &descriptor);
send.operation = 0x0000;
send.numdtaelmnts = 1;
/**----- Send the packet -------------**/
senddata (&send, buffer, descriptor, commhandle, line, linesiz);
if ((send.retcode != 0) || (send.reason != 0))
{
printf("Data NOT sent for commhandle %.9s\n", commhandle);
printf("Return code = %d\n", send.retcode);
printf("Reason code = %d\n", send.reason);
printf("new pcep %d\n", send.newpcep);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
i = i + 1;
printf("Data %d Sent for commhandle %.9s.\n\n", i, commhandle);
linesiz = getline(line, 92, fptr); /** Get next record **/
} /*** End While loop ***/
/***************************************************************/
/*************** Set up a Clear Request Packet **************/
/***************************************************************/
(9)
(10)
/***************************************************************/
/*********** Receive the Clear Request Response packet *****/
/***************************************************************/
/*-------- Set a timer to receive a message ---------**/
expctid = 0xF0F3;
settimer(&expctid, "Rv Clr Rqt", &dataq, &qname, commhandle);
if (expctid != 0xF0F3)
{
disablelink (&disable, commhandle, &qname);
return;
}
/*********** Call QOLRECV to Receive the Clear Response *****/
/**** Get pointers to the user spaces. ******/
QUSPTRUS (&inbuff, &buffer);
QUSPTRUS (&indesc, &descriptor);
QOLRECV (&(recv.retcode), &(recv.reason), &(recv.ucep),\
&(recv.pcep), &(recv.operation), &(recv.numdtaunits),\
&(recv.dataavail), &(recv.errorspecific), commhandle);
if ((recv.retcode != 0) || (recv.reason != 0))
{
printf("Recv clear response failed\n");
printf("return code %d\n", recv.retcode);
printf("reason code %d\n", recv.reason);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
/* Interpret the Received Operation */
if (recv.operation != 0xB101)
{
printf("Recvd opr %x instead of opr B101\n", recv.operation);
disablelink (&disable, commhandle, &qname);
return;
}
/***********************************************/
/*** Disable the link and end the program ****/
/***********************************************/
disablelink (&disable, commhandle, &qname);
printf("****** SOURCE completed successfully ******\n\n");
} /* End Main */
/*****************************************************************/
/************** Start Subroutine Section **********************/
/*****************************************************************/
/*************************************************************/
/*************** Send a Packet of Data ******************/
(11)
(12)
/********************************************/
/******** Routine to disable ***********/
void disablelink (disableparms *disable,
char *commhandle,
usrspace *qname)
{
unsigned short expctid;
qentry dataq;
disable->vary = 1; /* Hard coded to be varied off */
QOLDLINK (&(disable->retcode), &(disable->reason),\
commhandle, &(disable->vary));
if ((disable->retcode != 0) && (disable->reason != 00))
{
printf ("Link %.10s did not disabled.\n", commhandle);
printf ("return code = %d\n", disable->retcode);
printf ("reason code = %d\n\n", disable->reason);
}
/**------- Set a timer to receive disable complete msg --------**/
expctid = 0xF0F1;
settimer(&expctid, "Disable", &dataq, qname, commhandle);
if (expctid != 0xF0F1)
{
printf("Disable link did not complete successfully");
return;
}
printf ("%.10s link disabled \n", commhandle);
/** close the files **/
fclose(fptr);
fclose(screen);
} /* End disablelink Subroutine */
/**************************************************************/
/** Routine to convert string to Hexadecimal format ******/
void byte (char *dest,
int dlength,
char *source,
int slength)
{
register int counter;
char holder[2];
for (counter=0;counter<dlength;counter++)
dest[counter]=0;
for (counter=slength-1;counter>=0;counter--)
if (isxdigit(source[counter]))
{
holder[0]=source[counter];
holder[1]='\0';
if (counter % 2 == 0)
dest[counter/2] += (char) hextoint(holder)*16;
else dest[counter/2] += (char) hextoint(holder);
}
} /* End byte Subroutine */
/**************************************************************/
/** Routine to display the ErrorSpecific output ******/
void printespec(espec *errorspecific)
{
especout outparms;
/************************************************************/
/** x25lind: Retrieve X.25 line description information **/
void x25lind (qlindparms *qlind, char *linename)
{
register int counter;
for(counter=0;counter<256;counter++)
qlind->userbuffer[counter]=0;
qlind->format = 0x01;
QOLQLIND (&(qlind->retcode), &(qlind->reason), &(qlind->nbytes),\
qlind->userbuffer, linename, &(qlind->format));
} /* End x25lind Subroutine */
/*******************************************************/
/** Getline: Read a record into line and return length **/
int getline (char *line, int max, FILE *fptr)
{
if (fgets(line, max, fptr) == NULL)
return 0;
else
return strlen(line);
} /* End getline Subroutine */
/***************************************************/
The target application waits for the source application to initiate the file transfer. The following list summarizes the actions of the
target application:
● Calls the QOLQLIND API to get local X.25 line information
● Calls the QOLRECV API to receive the X'B201' operation (incoming call)
● Calls the QOLSEND API with a X'B400' operation to accept the SVC connection
● Receives the file from the target system using X'0001' operations
● Calls the QOLRECV API to receive the X'B301' (connection failure notification)
● Call the QOLSEND API with 'B100' operation to locally close the SVC connection
● Calls the QOLTIMER API to manage the reception of data queue entries
To create the program using ILE C, use the Create Bound C (CRTBNDC) command.
Explanations of the reference numbers in the listing can be found in Target application program listing references.
Program name . . . . . .
:(TARGET) . . .
Library name . . .: . . . .
UDCS_APPLS .
Source file . . . . .: . .
QCSRC . . .
Library name . . .: . . . .
UDCS_APPLS .
Source member name .: . .
TARGET . . .
Text Description . . . . . . .
: Target Application Example
Output . . . . . . .: . .
*NONE . . .
Compiler options . .: . . . . .
*SOURCE *NOXREF *NOSHOWUSR
: *NOSHOWSYS *NOSHOWSKP *NOEXPMAC
: *NOAGR *NOPPONLY *NODEBUG
: *GEN *NOSECLVL *PRINT *LOGMSG
: *USRINCPATH
Checkout Options . . . . . . . : *NOAGR
Optimization . . . . . . . . . : *NONE
Inline Options:
Inliner . . . . . . . . . . . : *OFF
Mode . . . . . . . . . . . . : *NOAUTO
Threshold . . . . . . . . . . : 250
Limit . . . . . . . . . . . . : 2000
Debugging View . . . . . . . . : *NONE
Define Names . . . . . . . . . : *NONE
Language Level . . . . . . . . : *SOURCE
Source Margins:
Left margin . . . . . . . . . : 1
Right margin . . . . . . . . : 32754
Sequence columns:
Left column . . . . . . . . . : *NONE
Right column . . . . . . . . :
Message flagging level . . . . : 0
Compiler messages:
Message limit . . . . . . . . : *NOMAX
Message limit severity . . . : 30
Replace Program Object . . . . : *YES
User Profile . . . . . . . . . : *USER
Authority . . . . . . . . . . . : *LIBCRTAUT
Target Release . . . . . . . . : *CURRENT
System includes . . . . . . . . : *YES
/*******************************************************************/
/** **/
/** Program Name: Target Application Program Example **/
/** **/
/** **/
/** Function: **/
/** This is the target application program example that uses **/
/** X.25 services provided by the user-defined communications **/
/** support to receive a simple file from the source application **/
/** program running on System A. This program performs the **/
/** following: **/
/** 01. Open the target file named OUTFILE. **/
/** 02. Call QOLQLIND to obtain local line information. **/
/** 03. Enable a link. **/
/** 04. Set a Filter on the enabled link. **/
/** 05. Receive a 'B101'X operation (incoming call). **/
/** 06. Send a 'B400'X operation (accept call). **/
/** 07. Receive '0001'X operation(s) (incoming data) from **/
/** the source application program and write it to the **/
/** file opened in step 1). **/
/** 08. Receive a 'B301'X operation (clear call indication). **/
/** 09. Send a 'B100'X operation to respond locally to the **/
/** clearing of the connection. **/
/** 10. Disable the link enabled in step 3). **/
/** **/
/** A data queue will be actively used to manage the operation **/
/** of this program. Data queue support will be used to monitor **/
/** for the completion of the enable and disable routines, as **/
/** well as timer expirations and incoming data. Timers are **/
/** used to ensure that there will never be an infinite wait on **/
/** the data queue. If a timer expires, the link enabled will **/
/** be disabled and the program will stop. **/
/** **/
/** **/
/** Inputs: **/
/** The program expects the following input parameters: **/
/** Line Name: This is the name of the line description **/
/** that will be used to call the QOLELINK API. **/
/** The line must be an X.25 line with at least **/
/** one SVC of type *SVCBOTH or *SVCIN. **/
/** **/
/** CommHandle: This is the logical name that will be used **/
/** to identify the link enabled. **/
/** **/
/** Remote DTE Address: The is the Local Network Address **/
/** of system A. **/
/** **/
/** **/
/** Outputs: **/
/** Current status of the file transfer will be provided when **/
/** running this program. If an error should occur, then a **/
/** message will be displayed indicating where the error occurred **/
/** and the program will end. If the program completes **/
/** successfully, a "successful completion" message will be **/
/** posted. **/
/** **/
/** Language: ILE C for OS/400 **/
/** **/
/** APIs used: QOLELINK, QUSPTRUS, QOLRECV, QOLSEND, QOLDLINK, **/
/** QRCVDTAQ, QOLTIMER **/
/** **/
/*******************************************************************/
/*******************************************************************/
/*******************************************************************/
/*******************************************************************/
#include "header"
#include "typedef"
#include "hexconv"
void senddata(sendparms *a, char *b, desc *c, char *d, char *e, int f);
void sndformat1(sendparms *a,char *b, char *c, char *d, qlindparms *e);
void sndformat2 (sendparms *a, char *b, char *c);
void setfilters (hdrparms *a);
void byte (char *a, int b, char *c, int d);
void printespec (espec *a);
void settimer(unsigned short *a,char *b,qentry *c,usrspace *d,char *e);
void dequeue (int a, char *b, qentry *c, usrspace *d);
void putdata (char *a, int b, FILE *c);
void x25lind (qlindparms *a, char *b);
void disablelink (disableparms *a, char *b, usrspace *c);
void handler (disableparms a, usrspace *b);
signal(SIGALL,SIG_DFL);
/** Clear the command line parameters **/
strncpy(enable.linename, " ", 10); /* Clear linename */
strncpy(commhandle, " ", 10); /* Clear Commhandle */
strncpy(rmtdte, " ", 17); /* Clear Remote DTE */
/** Receive command line Parameters **/
strncpy(enable.linename, argv[1], strlen(argv[1]));
strncpy(commhandle, argv[2], strlen(argv[2]));
strncpy(rmtdte, argv[3], strlen(argv[3]));
rmtdte[strlen(argv[3])] = '\0';
/** Initialize the user spaces **/
strncpy(inbuff.library, "UDCS_APPLS", 10); /* Input Buffer */
strncpy(inbuff.name, "TARGETIBUF", 10);
strncpy(indesc.library, "UDCS_APPLS", 10); /* Input B Desc */
strncpy(indesc.name, "TARGETIDSC", 10);
strncpy(outbuff.library, "UDCS_APPLS", 10); /* Output Buffer*/
strncpy(outbuff.name, "TARGETOBUF", 10);
strncpy(outdesc.library, "UDCS_APPLS", 10); /* Output B Desc */
strncpy(outdesc.name, "TARGETODSC", 10);
strncpy(qname.library, "UDCS_APPLS", 10); /* Data queue */
strncpy(qname.name, "X25DTAQ ", 10);
/***** retrieve the line description information ******/
x25lind (&qlind, enable.linename);
if ((qlind.retcode != 0) || (qlind.reason != 0))
{
printf("Query line description failed.\n");
printf("Return code = %d\n", qlind.retcode);
printf("Reason code = %d\n\n", qlind.reason);
return;
}
/***** Hard Code the QOLELINK Input Parameters ******/
enable.maxdtax25 = 512;
enable.keylength = 3;
strncpy(enable.keyvalue, "RCV", 3);
(2)
/******************************************************************/
/** Send a response to accept the call and establish a connection */
/******************************************************************/
/**** Get pointers to the user spaces. ******/
QUSPTRUS(&outbuff, &buffer);
QUSPTRUS(&outdesc, &descriptor);
/******* Set up Send Packet *********/
send.ucep = 62; /* set UCEP to be 62 */
send.pcep = recv.pcep; /* get the PCEP number */
send.operation = 0xB400; /* send a call request response*/
send.numdtaelmnts = 1; /* send one data unit */
/**----- Send the packet ----------------**/
sndformat1 (&send, buffer, rmtdte, commhandle, &qlind);
if ((send.retcode != 0) || (send.reason != 0))
{
printf("Data NOT sent for commhandle %.9s\n", commhandle);
printf("Return code = %d\n", send.retcode);
printf("Reason code = %d\n", send.reason);
printf("new pcep %d\n\n", send.newpcep);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
printf("An X.25 SVC connection was completed\n\n");
(7)
/**********************************************************/
/**** Receive Incoming Data *************************/
/**********************************************************/
/**------- Set a timer to receive data --------**/
expctid = 0xF0F3;
settimer(&expctid, "Inc Data ", &dataq, &qname, commhandle);
if (expctid != 0xF0F3)
{
disablelink (&disable, commhandle, &qname);
return;
}
/*******--- Receive the Incoming Data ----******/
/** Get pointer to user space **/
QUSPTRUS (&inbuff, &buffer);
QUSPTRUS (&indesc, &descriptor);
/** Receive the data **/
QOLRECV (&(recv.retcode), &(recv.reason), &(recv.ucep),\
&(recv.pcep), &(recv.operation), &(recv.numdtaunits),\
&(recv.dataavail), &(recv.errorspecific), commhandle);
if ((recv.retcode != 0) || (recv.reason != 0))
{
printf("Recv op for first data unit failed\n");
printf("return code %d\n", recv.retcode);
printf("reason code %d\n", recv.reason);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
(8)
/**************************************************************/
/******* Start a loop to read in all the incoming data ***/
/**************************************************************/
i = 1;
while (recv.operation == 0x0001)
{
printf("%d Data Recvd {%.4x}.\n\n", i++, recv.operation);
/** Store all the data units in the file **/
for (j = 1; j <= recv.numdtaunits; j++) {
putdata (buffer + (j - 1)*enable.tdusize,\
descriptor->length, fptr);
descriptor = (desc *)((char *)descriptor + sizeof(desc));
} /* for */
/**------- Set a timer to wait for more data -------**/
if (recv.dataavail == 0)
{
/** Set timer **/
expctid = 0xF0F3;
settimer(&expctid, "Wt Inc Dta", &dataq, &qname, commhandle);
if (expctid != 0xF0F3)
{
disablelink (&disable, commhandle, &qname);
return;
}
}
/** Get pointer to user space **/
QUSPTRUS (&inbuff, &buffer);
QUSPTRUS (&indesc, &descriptor);
/** Receive the data **/
QOLRECV (&(recv.retcode), &(recv.reason), &(recv.ucep),\
&(recv.pcep), &(recv.operation), &(recv.numdtaunits),\
&(recv.dataavail), &(recv.errorspecific), commhandle);
} /** End Receive data while loop ******/
(9)
/****************************************************/
/*********** Receive the Clear indication ***********/
/****************************************************/
if ((recv.retcode != 83) || (recv.reason != 4002))
{
printf("Recv opr for clear request failed\n");
printf("return code %d\n", recv.retcode);
printf("reason code %d\n", recv.reason);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
/* Interpret the Received Operation */
if (recv.operation != 0xB301)
{
printf("Recvd operation %x instead of B301", recv.operation);
disablelink (&disable, commhandle, &qname);
return; /**** end the program ***/
}
(10)
/****************************************************************/
/*********** Send local response to clear indication ***********/
/****************************************************************/
/**** Get pointers to the user spaces. ******/
QUSPTRUS(&outbuff, &buffer);
QUSPTRUS(&outdesc, &descriptor);
/******* Set up the packet ****************/
send.operation = 0xB100; /* send a clear request packet */
send.numdtaelmnts = 1; /* send one data unit */
/**----- Send the packet ----------------**/
sndformat2 (&send, buffer, commhandle);
if ((send.retcode != 0) && (send.reason != 0))
{
printf("Response not sent for clear connection\n");
printf("Return code = %d\n", send.retcode);
printf("Reason code = %d\n", send.reason);
printf("new pcep %d\n\n", send.newpcep);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
/******************************************************/
/*********** Receive the Clear Confirmation **********/
/******************************************************/
/**------- Set a timer to receive data --------**/
expctid = 0xF0F3;
settimer(&expctid, "Clr Cnfrm", &dataq, &qname, commhandle);
if (expctid != 0xF0F3)
{
disablelink (&disable, commhandle, &qname);
return;
}
if ((recv.retcode != 00) || (recv.reason != 0000))
{
printf("Recv failed for clear confirmation\n");
printf("return code %d\n", recv.retcode);
printf("reason code %d\n", recv.reason);
printespec(&(send.errorspecific));
disablelink (&disable, commhandle, &qname);
return;
}
/* Interpret the Received Operation */
if (recv.operation != 0xB101)
{
printf("Recvd opr %x instead of opr B301\n", recv.operation);
disablelink (&disable, commhandle, &qname);
return;
}
(11)
/****************************************/
/** disable the link and end program **/
/****************************************/
disablelink (&disable, commhandle, &qname);
printf("TARGET application completed OK!\n\n");
} /* End Main */
/*****************************************************************/
/************** Start Subroutine Section **********************/
/*****************************************************************/
/*****************************************************************/
/************ Routine to fill X.25 Format I ***********/
void sndformat1 (sendparms *send,
char *buffer,
char *rmtdte,
char *commhandle,
qlindparms *qlind)
{
format1 *output = (format1 *) buffer;
register int counter;
register querydata *qd;
qd = (querydata *)&(qlind->userbuffer);
output->type = 0; /* not used */
output->logchanid = 0x0;
output->sendpacksize = qd->x25data.defsend;
output->sendwindsize = qd->x25data.windowsend;
output->recvpacksize = qd->x25data.defrecv;
output->recvwindsize = qd->x25data.windowrecv;
output->dtelength = strlen(rmtdte); /* not used */
byte(output->dte, 16, rmtdte, strlen(rmtdte)); /* not used */
output->dbit = 0;
output->cug = 0; /* not used */
output->cugid = 0; /* not used */
output->reverse = 0; /* not used */
output->fast = 0; /* not used */
output->faclength = 0;
byte(output->facilities, 109, "", 0);
output->calllength = 0;
byte(output->callud, 128, "00", 2);
output->misc[0] = 0;
output->misc[1] = 0;
output->misc[2] = 0;
output->misc[3] = 0;
output->maxasmsize = 16383;
output->autoflow = 32;
QOLSEND (&(send->retcode), &(send->reason), &(send->errorspecific),\
&(send->newpcep), &(send->ucep), &(send->pcep),\
commhandle, &(send->operation), &(send->numdtaelmnts));
} /* End sndformat1 Subroutine */
/*****************************************************************/
/************ Routine to fill X.25 Format II ***********/
void sndformat2 (sendparms *send,
char *buffer,
char *commhandle)
{
format2 *output = (format2 *) buffer;
output->type = 1;
output->cause = 'FF';
output->diagnostic = 'FF';
output->faclength = 0;
byte(output->facilities, 109, "", 0);
output->length = 0;
byte(output->userdata, 128, "", 0);
QOLSEND (&(send->retcode), &(send->reason), &(send->errorspecific),\
&(send->newpcep), &(send->ucep), &(send->pcep),\
commhandle, &(send->operation), &(send->numdtaelmnts));
} /* End sndformat2 Subroutine */
/********************************************************************/
/************** Fill in the Buffer for the Filter ****************/
void setfilters (hdrparms *header)
{
x25filter *filters;
filters = (x25filter *)header->filters;
filters[0].pidlength = 1;
filters[0].pid = 0x21; /* set the protocol ID */
filters[0].dtelength = 0; /* no DTE used in filter */
byte(filters[0].dte, 12, "", 0);
filters[0].flags = 0x0;
filters[0].flags += 0x80; /* Set Reverse Charging to no */
filters[0].flags += 0x40; /* Set Fast Select to no */
} /* End setfilters Subroutine */
/********************************************/
/******** Routine to disable ***********/
void disablelink (disableparms *disable,
char *commhandle,
usrspace *qname)
{
qentry dataq;
unsigned short expctid;
disable->vary = 1; /* Hard code device to vary off */
/** Call disable link **/
QOLDLINK (&(disable->retcode), &(disable->reason),\
commhandle, &(disable->vary));
if ((disable->retcode != 0) && (disable->reason != 00))
{
printf ("Link %.10s did not disabled.\n", commhandle);
printf ("return code = %d\n", disable->retcode);
printf ("reason code = %d\n\n", disable->reason);
}
else
printf ("%.10s link disabled \n", commhandle);
/**------- Set a timer to receive message --------**/
expctid = 0xF0F1;
settimer(&expctid, "Disable ", &dataq, qname, commhandle);
if (expctid != 0xF0F1)
{
printf("Disable link did not complete successfully");
return;
}
/** close the files **/
fclose(fptr);
fclose(screen);
} /* End disablelink Subroutine */
/**************************************************************/
/** Routine to convert string to Hexadecimal format ******/
void byte (char *dest,
int dlength,
char *source,
int slength)
{
register int counter;
char holder[2];
for (counter=0;counter<dlength;counter++)
dest[counter]=0;
for (counter=slength-1;counter>=0;counter--)
if isxdigit(source[counter])
{
holder[0]=source[counter];
holder[1]='\0';
if (counter % 2 == 0)
dest[counter/2] += (char) hextoint(holder)*16;
else dest[counter/2] += (char) hextoint(holder);
}
} /* End byte Subroutine */
/**************************************************************/
/** Routine to display the ErrorSpecific output ******/
/**************************************************************/
void printespec(espec *errorspecific)
{
especout outparms;
The following three includes are used by both the preceding source and target programs. They are not in an OS/400 library.
/*******************************************************************/
/*******************************************************************/
/* Include Name: Header */
/* */
/* */
/* Function: */
/* Type define and declare the structures used to interface */
/* to the user-defined communications APIs. These structures */
/* are used by both the source and target application. */
/* */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: QOLDLINK, QOLELINK, QOLSEND, QOLRECV, QOLSETF, */
/* QOLTIMER, QUSPTRUS, QRCVDTAQ, QCLRDTAQ, QOLQLIND */
/* */
/*******************************************************************/
/*******************************************************************/
FILE *screen;
FILE *rptr;
FILE *fptr;
#include <qoldlink.h>
#include <qolelink.h>
#include <qolsend.h>
#include <qolrecv.h>
#include <qolsetf.h>
#include <qoltimer.h>
#include <qusptrus.h>
#include <qrcvdtaq.h>
#include <qclrdtaq.h>
#include <qolqlind.h>
The following typedef include has new type declarations used by both source and target programs.
/*******************************************************************/
/*******************************************************************/
/* Include Name: Typedef */
/* */
/* Function: */
/* Define the buffer spaces used to pass the data to the */
/* APIs. */
/* */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/*******************************************************************/
/*******************************************************************/
/*These definitions and C library #include files are either global, or
are used by multiple modules in the Open FM API driver.*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <xxcvt.h>
#include <string.h>
#include <ctype.h>
#include <recio.h>
/*******************************************************************/
/*******************************************************************/
/* Include Name: Hexconv */
/* */
/* Function: */
/* This include brings in procedures to convert hexadecimal */
/* to integer values and vice versa. */
/* */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/*******************************************************************/
/*******************************************************************/
#include <stdio.h>
{
sprintf(hex,"%.2X",decimal);
return(hex);
}
sscanf(hex,"%x",&decimal);
return(decimal);
}
#include <string.h>
#include <stdio.h>
#include <qtactldv.h>
#include <qusec.h>
/********************************************************************/
/* Typedef structure for QTACTLDV */
/********************************************************************/
typedef struct { /* QTACTLDV command data */
Qta_CTLD0100_t data; /* command data */
char cmd_str[6]; /* command string */
} cmd_struct;
/********************************************************************/
/* Typedef structure for Error code */
/********************************************************************/
typedef struct { /* Error code structure */
Qus_EC_t Edata; /* Error code data */
/* CPF67C8 excp data */
char dev_nam[10]; /* Device name */
char reason_cd[3]; /* Reason code */
char resv1[3]; /* Reserved */
} EC_struct;
/********************************************************************/
/* Constants */
/********************************************************************/
/********************************************************************/
/* Variables for QTACTLDV */
/********************************************************************/
char device[10]; /* device name */
char send_buff[256]; /* send buffer */
int send_buff_len; /* length of send buffer */
char recv_buff[256]; /* receive buffer */
int recv_buff_len; /* length of recv buffer */
int cmd_data_len; /* length of command data */
int i; /* counter variable */
/********************************************************************/
/* OPEN connection */
/********************************************************************/
send_buff_len = 0; /* no send buffer */
recv_buff_len = 0; /* no receive buffer */
cmd_data_len = 0; /* no command data */
EC.Edata.Bytes_Provided = 32; /* No exceptions */
/********************************************************************/
/* Send Diagnostic command */
/********************************************************************/
send_buff_len = 0; /* no send buffer */
recv_buff_len = 0; /* no recv buffer */
cmd_data_len = sizeof(Cmd); /* size of command struct */
EC.Edata.Bytes_Provided = 32; /* No exceptions */
Cmd.data.Data_transfer_direction = XFRNONE; /* No data transfer */
Cmd.data.Requested_transfer_length = 0; /* 0 transfer length */
Cmd.data.Ignore_length_errors = RPTLERR; /* report length errs */
Cmd.data.Command_timeout = 600; /* 10 minute timeout */
Cmd.data.Type_of_command = CMDSCSI; /* SCSI command */
Cmd.data.Offset_to_command_string = 32; /* offset 32 */
Cmd.data.Length_of_command_string = 6; /* 6 byte command */
Cmd.data.Reserved1=0; /* reserved */
memcpy(&Cmd.cmd_str, SNDDIAG, 6); /* command string */
else if (strncmp(EC.reason_cd,"\x02\xC1\x00", 3) == 0)
/* Selection timeout */
{
/* Send message that device might be powered off */
}
else
{
/* Send error message for unexpected reason code */
}
}
else
{
/* Handle other messages */
}
}
else
{
/* No error */
}
/********************************************************************/
/* CLOSE connection */
/********************************************************************/
send_buff_len = 0; /* no send buffer */
recv_buff_len = 0; /* no receive buffer */
cmd_data_len = 0; /* no command data */
EC.Edata.Bytes_Provided = 32; /* No exceptions */
}
Using a Data Queue
The following examples explain three methods to process data queue files.
In the following example, program B specifies to wait up to 2 hours (7200 seconds) to receive an entry from the data queue. Program
A sends an entry to data queue DTAQ1 in library QGPL. If program A sends an entry within 2 hours, program B receives the entries
from this data queue. Processing begins immediately. If 2 hours elapse without program A sending an entry, program B processes the
time-out condition because the field length returned is 0. Program B continues receiving entries until this time-out condition occurs.
The programs are written in CL; however, either program could be written in any high-level language.
PGM
DCL &FLDLEN *DEC LEN(5 0) VALUE(80)
DCL &FIELD *CHAR LEN(80)
.
.(determine data to be sent to the queue)
.
CALL QSNDDTAQ PARM(DTAQ1 QGPL &FLDLEN &FIELD)
.
.
.
PGM
DCL &FLDLEN *DEC LEN(5 0) VALUE(80)
DCL &FIELD *CHAR LEN(80)
DCL &WAIT *DEC LEN(5 0) VALUE(7200) /* 2 hours */
.
.
.
LOOP: CALL QRCVDTAQ PARM(DTAQ1
QGPL &FLDLEN &FIELD &WAIT)
IF (&FLDLEN *NE 0) DO /* Entry received */
.
. (process data from data queue)
.
GOTO LOOP /* Get next entry from data queue */
ENDDO
.
. (no entries received for
2 hours; process time-out condition)
.
Example 2: Waiting for Input from a Display File and an ICF File
The following example is different from the usual use of data queues because there is only one job. The data queue serves as a
communications object within the job rather than between two jobs.
In this example, a program is waiting for input from a display file and an ICF file. Instead of alternately waiting for one and then the
other, a data queue is used to allow the program to wait on one object (the data queue). The program calls QRCVDTAQ and waits for
an entry to be placed on the data queue that was specified on the display file and the ICF file. Both files specify the same data queue.
Two types of entries are put on the queue by display data management and ICF data management support when the data is available
from either file. ICF file entries start with *ICFF and display file entries start with *DSPF.
The display file or ICF file entry that is put on the data queue is 80 characters in length and contains the field attributes described in
the following table. Therefore, the data queue that is specified using the CRTDSPF, CHGDSPF, OVRDSPF, CRTICFF, CHGICFF,
and OVRICFF commands must have a length of at least 80 characters.
Position (and Data Type) Description
1 through 10 (character) The type of file that placed the entry on the data queue. This field will have one of two
values:
If the job receiving the data from the data queue has only one display file or one ICF file
open, then this is the only field needed to determine what type of entry has been received
from the data queue.
11 through 12 (binary) The unique identifier for the file. The value of the identifier is the same as the value in the
open feedback area for the file. This field should be used by the program receiving the
entry from the data queue only if there is more than one file with the same name placing
entries on the data queue.
13 through 22 (character) The name of the display file or ICF file. This is the name of the file actually opened, after
all overrides have been processed, and is the same as the file name found in the open
feedback area for the file. This field should be used by the program receiving the entry
from the data queue only if there is more than one display file or ICF file that is placing
entries on the data queue.
23 through 32 (character) The library where the file is located. This is the name of the library, after all overrides have
been processed, and is the same as the library name found in the open feedback area for the
file. This field should be used by the program receiving the entry from the data queue only
if there is more than one display file or ICF file that is placing entries on the data queue.
33 through 42 (character) The program device name, after all overrides have been processed. This name is the same
as that found in the program device definition list of the open feedback area. For file type
*DSPF, this is the name of the display device where the command or Enter key was
pressed. For file type *ICFF, this is the name of the program device where data is
available. This field should be used by the program receiving the entry from the data queue
only if the file that placed the entry on the data queue has more than one device or session
invited prior to receiving the data queue entry.
43 through 80 (character) Reserved.
The following example shows coding logic that the application program previously described might use:
.
.
.
.
OPEN DSPFILE ...
/* Open the Display file. DTAQ parameter specified on*/
/* CRTDSPF, CHGDSPF, or OVRDSPF for the file. */
.
.
DO
WRITE DSPFILE
/* Write with Invite for the Display file */
WRITE ICFFILE
/* Write with Invite for the ICF file */
CALL QRCVDTAQ
/* Receive an entry from the data queue specified */
/* on the DTAQ parameters for the files. Entries */
/* are placed on the data queue when the data is */
/* available from any invited device or session */
/* on either file. */
/* After the entry is received, determine which file */
/* has data available, read the data, process it, */
/* invite the file again and return to process the */
/* next entry on the data queue. */
IF 'ENTRY TYPE' FIELD = '*DSPF ' THEN
/* Entry is from display */
DO /* file. Since this entry*/
/* does not contain the */
/* data received, the data*/
/* must be read from the */
/* file before it can be */
READ DATA FROM DISPLAY FILE /* processed. */
PROCESS INPUT DATA FROM DISPLAY FILE
WRITE TO DISPLAY FILE /* Write with Invite */
END
ELSE /* Entry is from ICF */
Example 3: Waiting for Input from a Display File and a Data Queue
In the following example, the program in Job B is waiting for input from a display file that it is using and for input to arrive on the
data queue from Job A. Instead of alternately waiting for the display file and then the data queue, the program waits for one object,
the data queue.
The program calls QRCVDTAQ and waits for an entry to be placed on the data queue that was specified on the display file. Job A is
also placing entries on the same data queue. There are two types of entries put on this queue, the display file entry and the
user-defined entry. The display file entry is placed on the data queue by display data management when data is available from the
display file. The user-defined entry is placing on the data queue by Job A.
The structure of the display file entry is described in the previous example.
The structure of the entry placed on the queue by Job A is defined by the application programmer.
The following example shows coding logic that the application program in Job B might use:
.
.
.
.
OPEN DSPFILE ...
/* Open the Display file. DTAQ parameter specified on*/
/* CRTDSPF, CHGDSPF, or OVRDSPF for the file. */
.
.
DO
WRITE DSPFILE /* Write with Invite for the Display file */
CALL QRCVDTAQ
/* Receive an entry from the data queue specified */
/* on the DTAQ parameter for the file. Entries */
/* are placed on the data queue either by Job A or */
/* by display data management when data is */
/* available from any invited device on the display */
/* file. */
/* After the entry is received, determine what type */
/* of entry it is, process it, and return to receive */
/* the next entry on the data queue. */
IF 'ENTRY TYPE' FIELD = '*DSPF ' THEN
/* Entry is from display */
DO /* file. Since this entry*/
/* does not contain the */
/* data received, the data*/
/* must be read from the */
/* file before it can be */
READ DATA FROM DISPLAY FILE /* processed. */
PROCESS INPUT DATA FROM DISPLAY FILE
WRITE TO DISPLAY FILE
/* Write with Invite */
END
ELSE /* Entry is from Job A. */
/* This entry contains */
/* the data from Job A, */
/* so no read is required*/
/* before processing the */
/* data. */
PROCESS DATA QUEUE ENTRY FROM JOB A
LOOP BACK TO RECEIVE ENTRY FROM DATA QUEUE
.
.
.
END
Use the Create C Module (CRTCMOD) and the Create Program (CRTPGM) commands to create this program.
Call this program with one parameter to display the environment variable specified by that parameter. Call this program with two
parameters to set the environment variable specified by the first parameter to the value specified by the second parameter.
/******************************************************************/
/******************************************************************/
/* */
/* FUNCTION: Display the value of an environment variable and */
/* then set the environment variable to a new value. */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: getenv(), putenv() */
/* */
/******************************************************************/
/******************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
if (argc == 2) {
/* Called just to display the environment variables. */
envvar = getenv(argv[1]);
if (envvar == NULL) {
printf("No environment variable %s set\n",
argv[1]);
}
else {
printf("Environment variable: %s\n", envvar);
}
return 0;
}
Use the Create C Module (CRTCMOD) and the Create Program (CRTPGM) commands to create this program.
Call this program with one parameter (the file to store the variable list and the CCSIDs).
/******************************************************************/
/******************************************************************/
/* */
/* FUNCTION: Save the system-level environment variable list */
/* and the CCSIDs in a file */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: Qp0zGetAllSysEnv() */
/* */
/******************************************************************/
/******************************************************************/
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <qp0z1170.h>
if(argc != 2)
{
printf("Usage: call %s <filename>\n",argv[0]);
printf("Example: call %s '/tmp/sev'\n",argv[0]);
return -1;
}
sl = listBufSize = 1000;
sc = ccsidBufSize = 1000;
listBuf = (char *)malloc(listBufSize);
ccsidBuf = (int *)malloc(ccsidBufSize);
if(rc == ENOENT)
{
numvar = 0;
bw = write(fd, &numvar, sizeof(int));
close(fd);
printf("No system-level environment variables to save");
return 0;
}
if(rc != ENOSPC)
{
printf("Error using Qp0zGetAllSysEnv(), errno = %d\n", rc);
return -1;
}
rc = Qp0zGetAllSysEnv(listBuf, &listBufSize,
ccsidBuf, &ccsidBufSize, NULL);
if(rc != 0)
{
printf("Error using Qp0zGetAllSysEnv(), errno = %d\n", rc);
return -1;
}
}
numvar = ccsidBufSize/sizeof(int);
Use the Create C Module (CRTCMOD) and the Create Program (CRTPGM) commands to create this program.
Call this program with one parameter (the name of the file in which the system-level environment variables were stored).
/******************************************************************/
/******************************************************************/
/* */
/* FUNCTION: Restore the system-level environment variable list */
/* and the associated CCSIDs stored in a file */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: Qp0zPutSysEnv() */
/* */
/******************************************************************/
/******************************************************************/
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <qp0z1170.h>
if (argc != 2)
{
printf("Usage: call %s <filename>\n",argv[0]);
printf("Example: call %s '/tmp/sev'\n",argv[0]);
return -1;
}
/* Open the file specified */
fd = open(argv[1], O_RDONLY);
if(fd == -1)
{
printf("open() failed. errno = %d\n", errno);
return -1;
}
if(numvar > 0)
{
ccsidBufSize = numvar*sizeof(int);
ccsidBuf = (int *)malloc(ccsidBufSize);
listBuf += strlen(listBuf) + 1;
}
close(fd);
printf("System-level environment variables restored\n");
return 0;
PROCESS NOMONOPRC.
************************************************************
*
* This sample ILE COBOL program demonstrates how to call the
* Common Execution Environment (CEE) Date APIs. The program
* accepts two parameters. The first is the date in character
* form and the second the format of the date. For instance
* CALL CEEDATES ('10131955' 'MMDDYYYY') causes the program
* to treat the date as October 13 1955).
*
* The program then displays on the console the numeric day of
* the week for that date (Sunday = 1) and the named day of
* week for that date.
*
************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. CEEDATES.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
LINKAGE TYPE PROCEDURE FOR "CEEDAYS" USING ALL DESCRIBED,
LINKAGE TYPE PROCEDURE FOR "CEEDYWK" USING ALL DESCRIBED,
LINKAGE TYPE PROCEDURE FOR "CEEDATE" USING ALL DESCRIBED.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 Lilian-Date PIC S9(9) BINARY.
01 Day-of-Week-Numeric PIC S9(9) BINARY.
01 Day-of-Week-Alpha PIC X(10).
01 Day-of-Week-Format PIC X(10) VALUE "Wwwwwwwwwz".
LINKAGE SECTION.
01 Sample-Date PIC X(8).
01 Date-Format PIC X(8).
PROCEDURE DIVISION USING Sample-Date, Date-Format.
SAMPLE.
*
* Convert formatted date to Lilian date
*
CALL "CEEDAYS" USING Sample-Date
Date-Format
Lilian-Date
OMITTED.
*
* Get numeric day of week from Lilian date
*
CALL "CEEDYWK" USING Lilian-Date
Day-of-Week-Numeric
OMITTED.
*
* Get day of week from Lilian date
*
CALL "CEEDATE" USING Lilian-Date
Day-of-Week-Format
Day-of-Week-Alpha
OMITTED.
DISPLAY "Day of week = " Day-of-Week-Numeric UPON CONSOLE.
DISPLAY "Day of week = " Day-of-Week-Alpha UPON CONSOLE.
STOP RUN.
D************************************************************
D*
D* This sample ILE RPG program demonstrates how to call the
D* Common Execution Environment (CEE) Date APIs. The program
D* accepts two parameters. The first is the date in character
D* form and the second the format of the date. For instance
D* CALL CEEDATES ('10131955' 'MMDDYYYY') causes the program
D* to treat the date as October 13 1955).
D*
D* The program must be compiled with DFTACTGRP(*NO)
D*
D* The program then displays on the console the numeric day of
D* the week for that date (Sunday = 1) and the named day of
D* week for that date.
D*
D************************************************************
DLilianDate s 10i 0
DDayOfWkN s 10i 0
DDayOfWkA s 10
DDayOfWkFmt s 10 inz('Wwwwwwwwwz')
C *entry plist
C parm SampleDate 8
C parm DateFormat 8
C*
C* Convert formatted date to Lilian date
C*
C callb(d) 'CEEDAYS'
C parm SampleDate
C parm DateFormat
C parm LilianDate
C parm *OMIT
C*
C* Get numeric day of week from Lilian date
C*
C callb(d) 'CEEDYWK'
C parm LilianDate
C parm DayOfWkN
C parm *OMIT
C*
C* Get day of week from Lilian date
C*
C callb(d) 'CEEDATE'
C parm LilianDate
C parm DayOfWkFmt
C parm DayOfWkA
C parm *OMIT
C*
C DayOfWkN dsply
C DayOfWkA dsply
C eval *inlr = '1'
C return
Using the Generic Terminal APIs
The following two examples illustrate programs that implement a generic terminal and a simple interpreter.
Terminal Program
This program starts and runs a generic terminal.
This program demonstrates the use of the generic terminal functions Qp0zStartTerminal(), Qp0zRunTerminal(), Qp0zEndTerminal(),
and Qp0zGetTerminalPid().
Use the Create Bound C Program (CRTBNDC) command to create this program (see Creating the Terminal and Interpreter
Programs).
Call this program with no parameters (see Calling the Terminal Program).
/* Includes */
#include <qp0ztrml.h>
#include <qp0z1170.h>
#include <stdlib.h>
#include <stdio.h>
/* Constants */
#define NUM_PREDEFINED_ENVS 2
/* Global Variables */
extern char **environ;
/********************************************************************/
/* Build the argument array. */
/********************************************************************/
args[0] = "/QSYS.LIB/QGPL.LIB/ECHOINT.PGM";
args[1] = NULL;
/********************************************************************/
/* Build the environment variable array. */
/********************************************************************/
/********************************************************************/
/* Set the terminal attributes. */
/********************************************************************/
/********************************************************************/
/* Start and run the terminal. */
/********************************************************************/
default:
perror("Qp0zRunTerminal() failed");
exit(1);
break;
}
return 0;
}
Interpreter Program
This program is a simple echo interpreter that is used by the terminal program (see Terminal Program).
Use the Create Bound C Program (CRTBNDC) command to create this program (see Creating the Terminal and Interpreter
Programs).
/* Echo interpreter */
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
/* Do forever. */
while (1) {
/* Read a line from stdin. */
gets(buffer);
return 0;
}
void
SignalHandler(int signo)
{
printf("Ending for signal %d\n", signo);
exit(1);
}
CRTBNDC PGM(QGPL/TERMINAL)
SRCFILE(QGPL/QCSRC)
SRCMBR(TERMINAL)
SYSIFCOPT(*IFSIO)
TEXT('Example Terminal program')
CRTBNDC PGM(QGPL/INTERPRET)
SRCFILE(QGPL/QCSRC)
SRCMBR(INTERPRET)
SYSIFCOPT(*IFSIO)
TEXT('Example Interpreter program')
CALL PGM(QGPL/TERMINAL)
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: Illustrates how to generate, change, and release */
/* profile handles in a CL program. */
/* */
/* LANGUAGE: CL */
/* */
/* APIs USED: QSYGETPH - Get Profile Handle */
/* QWTSETP - Set Profile */
/* QSYRLSPH - Release Profile Handle */
/* */
/*******************************************************************/
/*******************************************************************/
/* Now change the user ID for this job back to the QSECOFR */
/* user ID: */
ENDPGM
The first thing to do, after deciding in what program object the exit point is to be placed, is to register that exit point. It is also
important to remember that the exit point format defines what the exit program data looks like. Here is an example ILE C program
that registers an exit point named QIBM_QXIC_TSTXPOINTA.
/********************************************************************/
/* PROGRAM: RegisterPoint */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* DESCRIPTION: This program registers an exit point in an */
/* application. */
/* */
/* APIs USED: QusRegisterExitPoint */
/* */
/********************************************************************/
#include <string.h>
#include <qusec.h>
#include <qusrgfa1.h>
/********************************************************************/
/* Structure for the control block */
/********************************************************************/
typedef _Packed struct Control_x{
int Num_Vlen_Recs;
Qus_Vlen_Rec_4_t Vlen_Rec_1;
char Description[50];
} Control_Rec;
int main () {
/*****************************************
*** INITIALIZING ALL STRUCTURES:: ***
*****************************************/
Error_Code.Bytes_Provided = sizeof(Error_Code);
EPnt_Controls.Num_Vlen_Recs = 1;
EPnt_Controls.Vlen_Rec_1.Length_Vlen_Record = 62;
EPnt_Controls.Vlen_Rec_1.Control_Key = 8;
EPnt_Controls.Vlen_Rec_1.Length_Data = 50;
memcpy( EPnt_Controls.Description , "Example Exit Point" , 17 );
QusRegisterExitPoint (EPnt_Name,
EPnt_F_Name,
&EPnt_Controls,
&Error_Code);
if ( Error_Code.Bytes_Available ) {
printf("\nEXCEPTION : %s",Error_Code.Exception_Id);
exit (1);
}
return(0);
}
After an exit point has been registered, exit programs must be added to that point, indicating the possible calls based on run-time
conditions. The following is an example in ILE C, of how to add an exit program to a registered exit point. The exit program named
TSTXITPROGQGPL is added to the exit point registered in the previous example named QIBM_QXIC_TSTXPOINTA.
/********************************************************************/
/* PROGRAM: AddProgram */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* DESCRIPTION: This program adds an exit program to a registered */
/* exit point. */
/* */
/* APIs USED: QusAddExitProgram */
/* */
/********************************************************************/
#include <qusec.h>
#include <qusrgfa1.h>
/********************************************************************/
/* Structure for the Exit Program Attributes */
/********************************************************************/
int main () {
Error_Code.Bytes_Provided=sizeof(Error_Code);
EProg_Attributes.Num_Vlen_Recs=1;
EProg_Attributes.ADPG_Vlen.Length_Vlen_Record=16;
EProg_Attributes.ADPG_Vlen.Control_Key=3;
EProg_Attributes.ADPG_Vlen.Length_Data=4;
EProg_Attributes.CCSID = 37;
QusAddExitProgram (EPnt_Name,
EPnt_F_Name,
EProg_Number,
Q_EProg_Name,
EProg_Data,
Len_EProg_Data,
&EProg_Attributes,
&Error_Code);
if ( Error_Code.Bytes_Available ) {
printf("\nEXCEPTION : %s",Error_Code.Exception_Id);
exit (1);
}
return(0);
}
When you have registered an exit point and have added the exit programs to that exit point, you can do exit program calls from within
your application. The information needed to do the calls is obtained from the Retrieve Exit Information API. In the following sample
a conditional call is made based on the exit point information.
/********************************************************************/
/* PROGRAM: RetrieveAndProcess */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* DESCRIPTION: This is an excerpt of a program that retrieves */
/* information on an exit point, and does processing */
/* based on that information. */
/* */
/* APIs USED: QusRetrieveExitInformation */
/* */
/********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <qusec.h>
#include <qusrgfa2.h>
/********************************************************************/
/* Structure for Selection Criteria on the Retrieve */
/********************************************************************/
typedef _Packed struct RTVEI_Select_C_x {
Qus_Selcrtr_t Crit;
Qus_Select_Entry_t Select_Entry;
char RTV_String[10];
} RTVEI_Select_C;
/********************************************************************/
/* Conv_Lib converts the library name to a null terminated string */
/********************************************************************/
char * Conv_Lib(char in_lib[], char *tmp) {
int x = 0;
int main() {
Qus_EXTI0200_t *EXTI0200;
Qus_EXTI0200_Entry_t *EXTI0200_Entry;
char *Pgm_Data;
int Counter;
char *tmp_str;
char *lib;
/*****************************************
* Initializing Structures *
*****************************************/
Error_Code.Bytes_Provided = sizeof(Error_Code);
tmp_str=(char *)malloc(sizeof(char));
lib=(char *)malloc(sizeof(char));
EXTI0200=(Qus_EXTI0200_t *) malloc ( ( sizeof( Qus_EXTI0200_t ) +
sizeof( Qus_EXTI0200_Entry_t ) + MAX_PGM_DATA_SIZE ) * 2 );
EProg_Select_C.Crit.Number_Sel_Criteria = 1;
EProg_Select_C.Select_Entry.Size_Entry = 26;
EProg_Select_C.Select_Entry.Comp_Operator = 1;
EProg_Select_C.Select_Entry.Start_Pgm_Data = 0;
EProg_Select_C.Select_Entry.Length_Comp_Data = 10;
memcpy( EProg_Select_C.RTV_String , "EXAMPLE " , 10 );
QusRetrieveExitInformation (Handle,
EXTI0200,
Length_Of_R_Var,
RTVEI_Format_Name,
EPnt_Name,
EPnt_F_Name,
EProg_Number,
&EProg_Select_C,
&Error_Code);
if ( Error_Code.Bytes_Available ) {
printf("\nEXCEPTION : %s",Error_Code.Exception_Id);
exit (1);
}
/********************************************************
* Call all of the preprocessing exit programs returned *
********************************************************/
Counter=EXTI0200->Number_Programs_Returned;
while ( Counter-- ) {
Conv_Lib(EXTI0200_Entry->Program_Library,lib);
return(0);
}
Server Program
This program acts as a server to the client program (see Client Program). The buffer is a shared memory segment. The process
synchronization is done using semaphores.
Use the Create C Module (CRTCMOD) and the Create Program (CRTPGM) commands to create this program.
Call this program with no parameters before calling the client program.
/*********************************************************************/
/*********************************************************************/
/* */
/* FUNCTION: This program acts as a server to the client program. */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: semctl(), semget(), semop(), */
/* shmat(), shmctl(), shmdt(), shmget() */
/* */
/*********************************************************************/
/*********************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
sarray[0] = 0;
sarray[1] = 0;
operations[1].sem_num = 0;
/* Operate on the first sem */
operations[1].sem_op = 1;
/* Increment the semval by 1 */
operations[1].sem_flg = IPC_NOWAIT;
/* Do not allow to wait */
Client Program
This program acts as a client to the server program (see Server Program). The program is run after the message Ready for client jobs
appears from the server program.
Call this program with no parameters after calling the server program.
/*********************************************************************/
/*********************************************************************/
/* */
/* FUNCTION: This program acts as a client to the server program. */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: semget(), semop(), */
/* shmget(), shmat(), shmdt() */
/* */
/*********************************************************************/
/*********************************************************************/
#include <stdio.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#define NUMSEMS 2
#define SIZEOFSHMSEG 50
operations[1].sem_num = 0;
/* Operate on the first sem */
operations[1].sem_op = 1;
/* Increment the semval by one */
operations[1].sem_flg = 0;
/* Allow a wait to occur */
operations[1].sem_num = 1;
/* Operate on the second sem */
operations[1].sem_op = 1;
/* Increment the semval by one */
operations[1].sem_flg = 0;
/* Allow a wait to occur */
return 0;
}
/*********************************************************************/
/*********************************************************************/
/* */
/* FUNCTION: */
/* This is a source application that uses the management services */
/* transport APIs. It does the following: */
/* 1. Prompts for the network ID and CP name of the remote system */
/* where target application MSTTARG has been started. */
/* 2. Prompts for data to be sent to MSTTARG. */
/* 3. Prompts for whether or not a reply is required. */
/* 4. Sends a management services transport request to MSTTARG. */
/* 5. Repeats steps 2-4 until QUIT is entered. */
/* */
/* Note: MSTTARG may be ended by this application by sending it the */
/* string "ENDRMTAPP". */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: QNMSTRAP, QNMENDAP, QNMRCVDT, */
/* QNMSNDRQ, QNMCHGMN, QNMENDAP */
/* */
/*********************************************************************/
/*********************************************************************/
/* Includes */
/*********************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NOERROR "NOERROR"
#define RQSONLY "*RQS "
#define RQSRPY "*RQSRPY "
/*-------------------------------------------------------------------*/
/* Type definitions */
/*-------------------------------------------------------------------*/
typedef int HANDLE; /* typedef for handle */
typedef char APPLNAME[8]; /* typedef for application name */
typedef char NETID[8]; /* typedef for network ID */
typedef char CPNAME[8]; /* typedef for control point name*/
typedef char MODENAME[8]; /* typedef for mode name */
typedef char SENSECODE[8]; /* typedef for SNA sense code (in
character format) */
typedef char LIBNAME[10]; /* typedef for library name */
typedef char QNAME[10]; /* typedef for data queue name */
typedef char MSGID[7]; /* typedef for message ID */
typedef char EXCPDATA[48]; /* typedef for exception data */
typedef char CATEGORY[8]; /* typedef for category */
typedef char APPLTYPE[10]; /* typedef for application type */
typedef char REPLREG[10]; /* typedef for replace
registration */
typedef char DATARCVD[10]; /* typedef for data received */
typedef char REQTYPE[10]; /* typedef for request type */
typedef char POSTRPL[10]; /* typedef for post reply */
typedef char REQUESTID[53]; /* typedef for request ID */
typedef char SRBUFFER[500]; /* typedef for send/receive
buffer. This program limits
the amount of data to be sent
or received to 500 bytes. The
maximum size of a management
services transport buffer is
31739. */
/*-------------------------------------------------------------------*/
/* External program declarations */
/*-------------------------------------------------------------------*/
#pragma linkage(QNMSTRAP, OS) /* Start application API */
extern void QNMSTRAP (HANDLE *handle, /* pointer to handle */
APPLNAME *applname, /* pointer to appl name */
QUALQNAME *qualqname, /* pointer to data queue
name */
ERRORCODE *errorcode); /* pointer to error code
parameter */
/*-------------------------------------------------------------------*/
/* Global declarations */
/*-------------------------------------------------------------------*/
HANDLE appl_handle; /* Handle of application */
ERRORCODE error_code_struc = /* Error code parameter */
{sizeof(error_code_struc), /* Initialize bytes provided */
0, /* initialize bytes available */
NOERROR}; /* initialize error code */
char input_line[80]; /* Input data */
QUALAPPL qual_appl = /* Qualified application name */
{" "," "," "};
REQUESTID req_id; /* Returned request ID */
int wait_time = -1; /* Wait time = wait forever */
/*-------------------------------------------------------------------*/
/* Start of main. */
/*-------------------------------------------------------------------*/
int main ()
{
APPLNAME appl_name = "MSTSOURC"; /* Application name to be used */
QUALQNAME data_queue_parm = /* Data queue name to be used */
{"*NONE ", " "}; /* Initialize structure */
NOTIFRCD notif_record; /* Area to contain notification
record */
CATEGORY category = "*NONE "; /* SNA/Management Services function
set group */
APPLTYPE appl_type = "*FPAPP "; /* Application type */
REPLREG replace_reg = "*YES "; /* Replace registration = *YES */
int sys_result; /* Result of system function */
char end_msg[] = "ENDRMTAPPL"; /* If this data is received then
the application will end */
char incoming_data[] = "01"; /* Incoming data constant */
SRBUFFER send_buffer; /* Send buffer */
int data_length; /* Length of send data */
char input_char; /* Input character */
REQTYPE req_type; /* Request type */
POSTRPL post_reply = "*NO "; /* Don't post any received replies
*/
MODENAME mode_name = "#INTER "; /* Mode name = #INTER */
/*-------------------------------------------------------------------*/
/* Start of code */
/*-------------------------------------------------------------------*/
QNMSTRAP (&appl_handle,
&appl_name,
&data_queue_parm,
&error_code_struc); /* Start application */
check_error_code("QNMSTRAP"); /* Check error code */
QNMCHGMN (&appl_handle,
&mode_name,
&error_code_struc); /* Change mode name */
check_error_code("QNMCHGMN"); /* Check error code */
get_network_id(); /* Get network ID */
get_cp_name(); /* Get CP name */
memcpy(qual_appl.app_name,
"MSTTARG ",
sizeof(qual_appl.app_name)); /* Copy application name */
printf ("Enter message to send to remote application or "
"QUIT to end\n");
gets(input_line);
while (memcmp(input_line,
"QUIT",
sizeof("QUIT")) != 0) /* While an ending string
has not been entered */
{
data_length = strlen(input_line); /* Get length of message */
memcpy(send_buffer,
input_line,
data_length); /* Put message in send buffer */
printf("Reply necessary? (Y or N)\n"); /* Prompt for reply
indicator */
gets(input_line); /* Get reply character */
input_char = toupper(input_line[0]); /* Convert character to
uppercase */
while (strlen(input_line) != 1 ||
(input_char != 'Y' &&
input_char != 'N'))
{
printf("Please type Y or N\n");
gets(input_line); /* Get reply character */
input_char = toupper(input_line[0]); /* Convert character to
uppercase */
}
if (input_char == 'Y')
{
memcpy(req_type,
RQSRPY,
sizeof(req_type)); /* Indicate request should have
a reply */
}
else
{
memcpy(req_type,
RQSONLY,
sizeof(req_type)); /* Indicate request should not have
a reply */
}
QNMSNDRQ (&appl_handle,
&qual_appl,
&req_id,
&send_buffer,
&data_length,
&req_type,
&post_reply,
&wait_time,
&error_code_struc); /* Send request to remote
application */
check_error_code("QNMSNDRQ"); /* Check error code */
if (input_char == 'Y')
{
process_replies(); /* Process one or more received
replies */
}
printf ("Enter message to send to remote application or "
"QUIT to end\n");
gets(input_line);
}
QNMENDAP (&appl_handle,
&error_code_struc); /* End the application */
return 0;
}
/*-------------------------------------------------------------------*/
/* process_replies function */
/*-------------------------------------------------------------------*/
void process_replies ()
{
RECEIVERVAR receiver_var = /* Receiver variable */
{sizeof(receiver_var)}; /* Initialize bytes provided */
int rcv_var_len = sizeof(receiver_var); /* Length of receiver
variable */
DATARCVD data_rcvd = "*NODATA "; /* Type of data received */
QUALAPPL qual_appl; /* Sender of reply */
/*-------------------------------------------------------------------*/
/* get_network_id function. */
/*-------------------------------------------------------------------*/
void get_network_id ()
{
int count;
printf("Enter network ID of remote system where MSTTARG "
"application has been started\n"); /* Prompt for network
ID */
gets(input_line); /* Get network ID */
while (strlen(input_line) <= 0 ||
strlen(input_line) > 8) /* While network ID is not valid */
{
printf("Network ID is too long or too short - try again\n");
gets(input_line); /* Get network ID */
}
memcpy(qual_appl.network_id,
input_line,
strlen(input_line)); /* Copy network ID */
for (count=0; count < strlen(input_line); count++)
qual_appl.network_id[count] =
toupper(qual_appl.network_id[count]); /* Convert
input to uppercase */
}
/*-------------------------------------------------------------------*/
/* get_cp_name function. */
/*-------------------------------------------------------------------*/
void get_cp_name ()
{
int count;
printf("Enter CP name of remote system where MSTTARG application "
"has been started\n"); /* Prompt for CP name */
gets(input_line); /* Get CP name */
while (strlen(input_line) <= 0 ||
strlen(input_line) > 8) /* While CP name is not valid */
{
printf("CP name is too long or too short - try again\n");
gets(input_line); /* Get CP name */
}
memcpy(qual_appl.cp_name,
input_line,
strlen(input_line)); /* Copy CP name */
for (count=0; count < strlen(input_line); count++)
qual_appl.cp_name[count] =
toupper(qual_appl.cp_name[count]); /* Convert
input to uppercase */
}
/*-------------------------------------------------------------------*/
/* check_error_code - */
/*-------------------------------------------------------------------*/
void check_error_code (char func_name[8])
{
char *sense_ptr = error_code_struc.exception_data + 36; /*
Pointer to sense code in
exception data */
SENSECODE sense_code; /* SNA sense code */
if (error_code_struc.bytes_available != 0) /* Error occurred? */
{
printf("\n\nError occurred calling %1.8s.\n",func_name);
memcpy(sense_code,
sense_ptr,
sizeof(sense_code)); /* Copy sense code from exception
data */
printf("Error code is %1.7s, SNA sense code is %1.8s.\n",
error_code_struc.exception_ID,
sense_code);
if (memcmp(func_name,
"QNMSTRAP",
sizeof(func_name)) != 0) /* Error did not occur on
start application? */
{
QNMENDAP (&appl_handle,
&error_code_struc); /* End the application */
}
exit(EXIT_FAILURE); /* Exit this program */
}
}
/*********************************************************************/
/*********************************************************************/
/* */
/* FUNCTION: */
/* This is a target application that uses the management services */
/* transport APIs. It receives management services transport */
/* requests from source application MSTSOURC and displays the data */
/* contained in the request. If the request specifies that a */
/* reply needs to be sent, this program accepts input from the */
/* keyboard and sends one or more replies to the source application. */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: QNMSTRAP, QNMENDAP, QNMREGAP, QNMDRGAP, */
/* QNMRCVDT, QNMSNDRP, QNMRCVOC, QRCVDTAQ, */
/* QNMENDAP */
/* */
/*********************************************************************/
/*********************************************************************/
/* Includes */
/*********************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NOERROR "NOERROR"
#define REQUEST "*RQS "
#define REQREPLY "*RQSRPY "
#define REPLYINC "*RPYINCPL "
#define REPLYCMP "*RPYCPL "
/*-------------------------------------------------------------------*/
/* Type definitions */
/*-------------------------------------------------------------------*/
typedef int HANDLE; /* typedef for handle */
typedef char APPLNAME[8]; /* typedef for application name */
typedef char NETID[8]; /* typedef for network ID */
typedef char CPNAME[8]; /* typedef for control point name*/
typedef char SENSECODE[8]; /* typedef for SNA sense code
(in character format) */
typedef char LIBNAME[10]; /* typedef for library name */
typedef char QNAME[10]; /* typedef for data queue name */
typedef char MSGID[7]; /* typedef for message ID */
typedef char EXCPDATA[48]; /* typedef for exception data */
typedef char CATEGORY[8]; /* typedef for category */
typedef char APPLTYPE[10]; /* typedef for application type */
typedef char REPLREG[10]; /* typedef for replace
registration */
typedef char DATARCVD[10]; /* typedef for data received */
typedef char REPLYTYPE[10]; /* typedef for reply type */
typedef char REQUESTID[53]; /* typedef for request ID */
typedef char PACKED5[3]; /* typedef for PACKED(5,0) field */
typedef char SRBUFFER[500]; /* typedef for send/receive
buffer. This program limits
the amount of data to be sent
or received to 500 bytes. The
maximum size of a management
services transport buffer is
31739. */
/*-------------------------------------------------------------------*/
/* External program declarations */
/*-------------------------------------------------------------------*/
#pragma linkage(QNMSTRAP, OS) /* Start application API */
extern void QNMSTRAP (HANDLE *handle, /* pointer to handle */
APPLNAME *applname, /* pointer to application
name */
QUALQNAME *qualqname,/* pointer to data queue
name */
ERRORCODE *errorcode); /* pointer to error code
parameter */
/*-------------------------------------------------------------------*/
/* Global declarations */
/*-------------------------------------------------------------------*/
HANDLE appl_handle; /* Handle of application */
ERRORCODE error_code_struc = /* Error code parameter */
{sizeof(error_code_struc), /* Initialize bytes provided */
0, /* initialize bytes available */
NOERROR}; /* initialize error code */
/*-------------------------------------------------------------------*/
/* Start of main function */
/*-------------------------------------------------------------------*/
int main ()
{
/*-------------------------------------------------------------------*/
/* Local declarations */
/*-------------------------------------------------------------------*/
APPLNAME appl_name = "MSTTARG "; /* Application name to be used */
QUALQNAME data_queue_parm = /* Data queue name to be used */
{"MSTDTAQ ", "QTEMP "}; /* Initialize structure */
NOTIFRCD notif_record; /* Area to contain notification
record */
RECEIVERVAR receiver_var = /* Receiver variable */
{sizeof(receiver_var)}; /* Initialize bytes provided */
QUALAPPL qual_appl; /* Qualified application name */
DATARCVD data_rcvd; /* Type of data received */
CATEGORY category = "*NONE "; /* SNA/Management Services function
set group */
APPLTYPE appl_type = "*FPAPP "; /* Application type */
REPLREG replace_reg = "*YES "; /* Replace registration = *NO */
REPLYTYPE reply_cmp = REPLYCMP; /* Complete reply */
REPLYTYPE reply_inc = REPLYINC; /* Incomplete reply */
int sys_result; /* Result of system function */
int rcv_var_len = sizeof(receiver_var); /* Length of receiver
variable */
PACKED5 wait_time_p = "\x00\x00\x1D"; /* Packed value for wait time
= -1, that is, wait forever */
PACKED5 record_len; /* Length of received data queue
record */
int wait_forever = -1; /* Integer value for wait time =
-1, that is, wait forever */
int no_wait = 0; /* Do not wait for I/O to
complete */
char end_msg[] = "ENDRMTAPPL"; /* If this data is received then
the application will end */
char incoming_data[] = "01"; /* Incoming data constant */
char inbuf[85]; /* Input buffer */
SRBUFFER send_buffer; /* Send buffer for sending
replies */
int reply_len; /* Length of reply data */
/*-------------------------------------------------------------------*/
/* Start of executable code */
/*-------------------------------------------------------------------*/
sys_result = system("DLTDTAQ DTAQ(QTEMP/MSTDTAQ)"); /* Delete
previous data queue (if any) */
sys_result = system("CRTDTAQ DTAQ(QTEMP/MSTDTAQ) MAXLEN(80)"); /*
Create data queue */
QNMSTRAP (&appl_handle,
&appl_name,
&data_queue_parm,
&error_code_struc); /* Start application */
check_error_code("QNMSTRAP"); /* Check error code */
QNMREGAP (&appl_handle,
&category,
&appl_type,
&replace_reg,
&error_code_struc); /* Register the application */
check_error_code("QNMREGAP"); /* Check error code */
while (memcmp(receiver_var.received_data,
end_msg,
sizeof(end_msg)) != 0)
{ /* Loop until an ending string
has been sent by the requesting
application */
QRCVDTAQ (&data_queue_parm.data_queue_name,
&data_queue_parm.library_name,
&record_len,
¬if_record,
&wait_time_p); /* Receive indication from data
queue */
if (memcmp(notif_record.function,
incoming_data,
sizeof(incoming_data)) == 0) /* Incoming data was
received? */
{
strncpy(receiver_var.received_data,
"\0",
sizeof(receiver_var.received_data)); /* Null out the
receive buffer */
QNMRCVDT (&appl_handle,
&receiver_var,
&rcv_var_len,
¬if_record.req_id,
&qual_appl,
&data_rcvd,
&wait_forever,
&error_code_struc); /* Receive data using the
request ID in the notification*/
check_error_code("QNMRCVDT"); /* Check error code */
printf("%1.500s\n",receiver_var.received_data); /* Display
the received data */
if (memcmp(data_rcvd,
REQREPLY,
sizeof(data_rcvd)) == 0) /* Request requires
a reply? */
{
printf("Please enter your replies (a null line "
"indicates that you are finished)\n"); /* Display
a prompt message */
gets(inbuf); /* Get the reply data */
reply_len = strlen(inbuf); /* Get length of reply */
while (reply_len != 0) /* While no null string was input
*/
{
memcpy(send_buffer,inbuf,strlen(inbuf)); /* Copy
data to send buffer */
QNMSNDRP (&appl_handle,
¬if_record.req_id,
&send_buffer,
&reply_len,
&reply_inc,
&no_wait,
&error_code_struc); /* Send a reply to the
source application (specify
"not last" reply). The results
of this operation will be
obtained later using the
receive operation completion
API. */
gets(inbuf); /* Get the next reply */
reply_len = strlen(inbuf); /* Get length of reply */
}
QNMSNDRP (&appl_handle,
¬if_record.req_id,
&send_buffer,
&reply_len,
&reply_cmp,
&no_wait,
&error_code_struc); /* Send final reply (this
contains no data). The results
of this operation will be
obtained later using the
receive operation completion
API. */
}
else
{ /* A reply is not required */
if (memcmp(data_rcvd,
REQUEST,
sizeof(data_rcvd)) != 0) /* Something other than a
request was received? */
{
printf("Incorrect data was received, "
"data_rcvd = %1.10s\n", data_rcvd); /* Print
value of data_rcvd */
}
}
}
else
{ /* A send completion was received
for a previous send reply
operation */
QNMRCVOC (&appl_handle,
¬if_record.req_id,
&qual_appl,
&error_code_struc);/* Receive operation completion*/
check_error_code("QNMRCVOC"); /* Check error code */
printf("Reply was sent successfully.\n"); /* Error code was
OK */
}
}
QNMDRGAP (&appl_handle,
&error_code_struc); /* Deregister the application */
QNMENDAP (&appl_handle,
&error_code_struc); /* End the application */
return 0;
}
/*-------------------------------------------------------------------*/
/* check_error_code - */
/* */
/* This function validates the error code parameter returned on */
/* the call to a management services transport API program. If */
/* an error occurred, it displays the error that occurred and */
/* ends this program. */
/*-------------------------------------------------------------------*/
void check_error_code (char func_name[8])
{
char *sense_ptr = error_code_struc.exception_data + 36; /*
Pointer to sense code in
exception data */
SENSECODE sense_code; /* SNA sense code */
if (error_code_struc.bytes_available != 0) /* Error occurred? */
{
printf("\nError occurred calling %1.8s.\n",func_name);
memcpy(sense_code,
sense_ptr,
sizeof(sense_code)); /* Copy sense code from exception
data */
printf("Error code is %1.7s, SNA sense code is %1.8s.\n",
error_code_struc.exception_ID,
sense_code);
if (memcmp(func_name,
"QNMSTRAP",
sizeof("QNMSTRAP")) != 0) /* Error did not occur on
start application? */
{
QNMENDAP (&appl_handle,
&error_code_struc); /* End the application */
}
exit(EXIT_FAILURE); /* Exit this program */
}
}
● The writer of an ILE compiler on the iSeries server might want to write a debugger to take advantages of the features of the
language. The OS/400 debugger is a more general-purpose debugger made for all ILE languages.
● A debugger could be written with functions not available on the OS/400 ILE debugger.
Besides APIs, there are two user exits that get called:
● The Source Debug program gets called when the Start Debug (STRDBG), Display Module Source (DSPMODSRC), and End
Debug (ENDDBG) CL commands are entered.
● The Program Stop Handler gets called when an ILE program being debugged hits a breakpoint, step, and so on.
To demonstrate how these APIs are used, this topic presents an example debugger with complete code examples and an explanation
of what the APIs do.
The ILE debugger that comes with OS/400 uses the debugger APIs just as a user-written debugger would. There is nothing special
about the OS/400 debugger. Its functions could be done by an application developer using the debugger APIs and other OS/400 APIs.
A Simple Debugger--Scenario
Consider a simple scenario in which the user wishes to debug an ILE program.
1. From the command entry screen, the user enters the Start Debug (STRDBG) command, passing it the name of an ILE
program to debug.
STRDBG P1
2. The ILE debugger screen is displayed, showing the source of a module in the ILE program being debugged. From this screen,
the user adds a breakpoint and then exits.
3. Back at the command entry screen, the user runs the ILE program that is being debugged.
CALL P1
4. The ILE program hits the breakpoint previously set. The ILE debugger screen is displayed, highlighting in the source where
the program has stopped at the breakpoint.
6. The user exits the ILE debugger, allowing the ILE program to run to completion. The program ends.
7. Back at the command entry screen, the user ends the debug session.
ENDDBG
This is the simplest of debug scenarios, but it illustrates how OS/400, the debugger user exits, and the debugger APIs interact.
When the Source Debug program is called, it is passed a reason field, which indicates why it was called. The *START reason
is passed to it by the Start Debug command, indicating that the ILE debugger is to start itself and do any necessary
initialization. When the *START reason is indicated, the names of any ILE programs on the Start Debug command are also
passed to the Source Debug program.
2. In this scenario, the system Source Debug program initializes itself. It calls the QteStartSourceDebug API, which tells the
system that ILE debugging is to be done. The name of a program stop handler program is passed to this API. The stop
handler is a program that the system calls when an ILE program hits a breakpoint, step, or other condition where the system
stops the program for the debugger.
The Source Debug program must indicate to the system that the ILE programs specified on the Start Debug command are to
be debugged. To do this, the QteRetrieveModuleViews API is called, once for each ILE program specified on the Start
Debug command. In this scenario, the API is called, passing it the name of program P1. The purpose of the API is to return
information about the ILE program, including the modules and views of the program. A view is the source text that is
displayed by the debugger for a particular module.
Once information about the ILE program is obtained, one or more views of the program must be registered. Once a view is
registered, the system can perform various functions on that view in behalf of the debugger application. For performance
reasons, only the views the user is interested in displaying should be registered.
The Source Debug program is now done performing the function for the *START reason. It exits, returning control to the
Start Debug command.
3. By default, if an ILE program is specified on the Start Debug command, the ILE debug screen is displayed. To indicate to the
ILE debugger that a screen is to be put up, the Source Debug program is called by the command again, this time with a
reason of *DISPLAY.
Because this is the first time any views for P1 are to be displayed, the ILE debugger must retrieve the text to display. The
first view of the first module of the program is selected as the default view to display.
The Source Debug program calls the QteRetrieveViewText API to retrieve the text associated with the default view. Next, in
case this program is already on the stack and stopped, the QteRetrieveStoppedPosition API is called to check. If the program
were on the stack, the source would be positioned to the statement where the program was stopped, and that line would be
highlighted. In this scenario, the program is not yet on the stack, so the first line of the source will appear at the top of the
screen, and no line will be highlighted.
The Source Debug program next calls User Interface Manager (UIM) APIs to display the source on the screen.
4. At this point, the source screen is displayed showing the text of the first view in the first module of the first ILE program
specified on the Start Debug command. From this screen, the user can enter debug commands or do other options provided
by the debugger application.
In this scenario, the user adds a breakpoint to a line in the ILE program P1 being debugged. When a command is entered, the
UIM APIs call a program which is part of the ILE debugger to process the command.
To process the breakpoint, the QteAddBreakpoint is called. It is passed a view number which indicates the view being
displayed, and a line number in that view. A breakpoint is added to the program by the API.
5. Back to the UIM screen, the user exits the ILE debugger. Once at the command entry screen, the user then runs the program
P1 which has the breakpoint.
6. When P1 hits the breakpoint, the system calls the program stop handler defined by the QteStartSourceDebug API. The
Program Stop Handler calls UIM to put up the source for the module where the program has stopped because of the
breakpoint. The line is highlighted to show the user exactly where the program has stopped.
7. From the source debugger screen, the user displays a variable in program P1 which is stopped at the breakpoint. UIM calls
the debugger to process the command. The debugger calls the QteSubmitDebugCommand API, which retrieves the value of
the variable to be displayed. The debugger then displays this value on the screen.
8. The user now exits from the source debugger screen. This allows P1, which was stopped at a breakpoint, to continue running.
When P1 ends, the user is back at the command entry screen.
9. The user ends the debug session by entering the End Debug (ENDDBG) CL command. The system calls the Source Debug
program, passing it a reason of *STOP. The Source Debug program calls the QteEndSourceDebug API to indicate to the
system that ILE debugging has ended. It then tears down its own environment (closes files, frees space, and so on) and then
ends. The End Debug command completes, and the user is back to the command entry, the debug session having ended.
Source Debugger--Example
This section discusses an example ILE debugger that demonstrates the use of some of the ILE debugger APIs. Each function in the C
program is discussed along with the APIs that they call. Although the entire program listing is printed later (see Debugger
Code--Sample), each function or piece of code is printed with the section where it is discussed to make reading the code easier.
The example debugger does not use all ILE debugger APIs. Its function is limited. After the discussion of the code, the APIs and
some functions not covered are discussed.
The Create C Module (CRTCMOD) command compiles the source code of the debugger. It is compiled into module DEBUG.
The Create Program (CRTPGM) command creates program DEBUG from module DEBUG. It is necessary to bind to service
program QTEDBGS so that the calls to the debugger APIs are resolved. It is also important to use activation group QTEDBGAG.
This is an activation group that cannot be destroyed while the job is in debug mode. Thus, all static variables in program DEBUG
remain intact throughout the debugging of the ILE program. Only when ENDDBG is entered can the activation group be destroyed,
even if the Reclaim Resources (RCLRSC) CL command is entered.
The example debugger consists of a single program called DEBUG. The program is used as the Source Debug program as well as the
Program Stop Handler. The program determines how many parameters it is being called with, and with this information it does the
function of one or the other of the user exits.
The debugger can debug only one ILE program. This program is specified on the Start Debug CL command. The program cannot be
removed from debug until ENDDBG is done. No new programs can be added.
To debug an ILE program P1 with this sample debugger, the following CL command could be entered:
STRDBG P1 SRCDBGPGM(DEBUG)
Note that DEBUG must be in the library list when STRDBG is done.
If the command is done, P1 is called twice, once as a Source Debug program given a reason of *START, and again as a Source
Debug program given a reason of *DISPLAY.
Other variations of the Start Debug command can be given with different results. For example, the following CL command causes
DEBUG to be called only once with a reason of *START:
This is because STRDBG has been told not to display the debug screen, so the *DISPLAY reason is not given until the user does the
Display Module Source (DSPMODSRC) CL command.
STRDBG SRCDBGPGM(DEBUG)
This is because no ILE program is specified. If an ILE program receives an unmonitored message and the ILE debugger needs to be
called, DEBUG is first called with *START as a Source Debug program. Also, if Display Module Source is entered, the *START
and then the *DISPLAY reason is passed to DEBUG.
When the debugger is started, it allows simple debugging commands to be entered. The C session manager is put up, which scrolls
the users commands and the debugger output. To see a list of the allowable commands, enter HELP.
The "list views" command shows all of the views available in the program being debugged. The text description of the view is listed,
with a sequential number. This number is used by the "switch" command to switch to that view.
The "list text" command prints out the text of the current view. Text has a line number next to it. The line number is used when
setting breakpoints or other debug commands.
The switch command switches the current view. The current view is the view used when setting breakpoints, displaying variables,
viewing text, and so on.
Other commands are interpreted by the QteSubmitDebugCommand API. This API will be discussed later. An example command that
can be entered is "break n", where n is the line number in the current view. These commands are similar to the ones allowed in the
ILE debugger shipped with OS/400.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <qtedbgs.h>
Besides the normal C library header files, an API header file, qtedbgs.h is included. This file defines the functions exported from
service program QTEDBGS. This service program contains the ILE debugger APIs.
Global Variables
These are global variables that hold information about the program being debugged. These variables do not go away when program
DEBUG exits, because they are stored in the activation group which is not destroyed until the debug session has completed.
The name and library of the program are stored, as is the current view being debugged. Also, a pointer to a structure returned by the
QteRetrieveModuleViews is saved, as this information is needed when debugging the various views of the program.
PgmList_t
typedef struct {
_TE_OBJLIB_T PgmLib; /* Name and Library of program */
_TE_NAME_T PgmType; /* program type, *PGM or *SRVPGM */
} PgmList_t;
This is the structure of the name, library, and type of the program being debugged.
main()
Program DEBUG can be called in two ways. When it is called by the STRDBG, DSPMODSRC, and ENDDBG CL commands, it is
called as the Source Debug program user exit. It is passed three parameters.
DEBUG can also be called when a program being debug hits a breakpoint or step. In this case, it is passed seven parameters.
DEBUG therefore can determine why it was called by counting the number of parameters it was passed. Remember that argc includes
the program name as the first argument passed.
If argc is 4 (three parameters passed to DEBUG), function HandleSession is called, and the three parameters passed to DEBUG are
passed to it, typecasted as needed.
If argc is 8 (seven parameters passed to DEBUG), function HandleStop is called, and the seven parameters passed to DEBUG are
passed to it, typecasted as needed.
If any other number of parameters are passed to DEBUG, it cannot have been called from the OS/400 debug support, so DEBUG will
just exit.
HandleSession()
When DEBUG is called as a session handler, it is passed three parameters. The first parameter is a 10-character array containing a
reason field. This contains the reason why the session handler is called.
When DEBUG is first called, it is passed a reason of *START, indicating that the debugger is to initialize for an ILE debug session.
When this reason is given, the second parameter contains a list of ILE programs specified on the STRDBG command, and the third
parameter contains the number of programs specified on parameter two. From 0 to 10 ILE programs can be specified.
When the user wishes to see the ILE debugger screen, either from STRDBG or DSPMODSRC, a reason of *DISPLAY is passed.
When the user enters ENDDBG, the *STOP reason is passed, indicating that the ILE debug session is ending. The second and third
parameters are not used when the reason is *DISPLAY or *STOP.
The code tests for a reason and calls the appropriate function. There is one function for each reason that can be passed.
TearDownDebugger()
void TearDownDebugger(void) {
_TE_ERROR_CODE_T errorCode = {8}; /* errors will be ignored */
This function is called when the user enters ENDDBG. The debugger calls the QteEndSourceDebug API which ends ILE debugging.
Since an 8 is passed as the number of bytes provided, the message ID and error data from an error are not returned to the caller. Thus,
any errors from this API (there should not be any) are ignored.
The exit() function is called, which destroys the activation group. Thus, all global data defined in the program's variables are lost.
This is ok, since the debug session is ending at this point.
StartUpDebugger()
This function is passed the second and third parameters which were passed from the system when it called DEBUG with a reason of
*START. These parameters are the list of programs to be added to debug and the number of programs in the list. This simple
example debugger can only debug one program, so if any other number of programs were specified on STRDBG, the debugger just
exits.
StartUpDebugger first stores the program/library element passed to it in a global variable available to all functions. This is the name
and library of the program being debugged. It then calls the QteStartSourceDebug API to tell the system that an ILE debug session is
to begin. The name and library of program DEBUG are passed to this API as the Program Stop Handler. Thus, whenever the program
being debugged is stopped by the debugger, program DEBUG will be called.
Finally, the function calls AddProgram to add the single program to debug.
AddProgram()
void AddProgram(void) {
The heart of this function is the two calls to the QteRetrieveModuleViews API and the call to QteRegisterDebugView API.
The QteRetrieveModuleViews API returns information about an ILE program. It returns this information in a structure of type
_TE_VEWL0100_T. This is a fairly complex structure that has the following fields:
This structure has a header portion which holds the number of bytes returned by the API (BytesReturned), the number of bytes that
can be returned by the API, used when there is not enough room for the API to return all of its data (BytesAvailable), and the number
of elements (views) returned by the API (NumberElements).
Since there is no way to know in advance how many views a program has, the QteRetrieveModuleViews API should be called once
with only enough storage to return the number of bytes that the API needs to return all of its information. Thus, the first call to the
API provides only 8 bytes of storage for the API to return its data. This allows the API to fill in the BytesAvailable field.
QteRetrieveModuleViews is passed a buffer to hold the receiver variable and the length of that buffer (in this case, 8 bytes). It is also
passed a format name which identifies the structure of the receiver variable. The only allowable format name at this time is
VEWL0100. A structure containing the program name and library name of the ILE program is passed. Also, the program type is
passed. In this example debugger, only *PGM objects can be debugged, but it is possible to debug *SRVPGM objects using the ILE
debugger APIs.
The name of the module is provided, in which case information about that module is returned. *ALL indicates that information about
all modules in the program is to be returned. A return library variable is passed. This is so that when *LIBL is passed as a library
name, the real library name can be obtained, making subsequent API calls faster because the library list won't have to be searched
again.
Finally an error code structure is passed to the API. This structure is initialized with a zero, indicating that the API is not to fill in any
error code data. Instead, the API issues an exception if an error occurs. No errors are expected, so this should not matter.
Before QteRetrieveModuleViews is called again, a buffer large enough to hold all of the information is created. The API is called
again with the same parameters, but this time the entire information will be stored by the API in the allocated buffer.
If the API does not return any elements, this means that none of the modules has debug data. In this case, the program cannot be
debugged, so the debug session is ended.
Now that a list of views has been retrieved, it is time to register all of the views to the system, making it possible to do debug
operations against them. In a real debugger, only the views requested to be seen by the user would be registered to save processing
time, but in this example, all views will be registered at once.
Not all of the fields in the VEWL0100 structure are needed by this debugger. However, they will be described here. The API returns
one element for each view in the program. Each module in the program may have several views. All views for a particular module are
contiguous in the list.
ModuleName This is the name of the module in the program which this particular view is for.
ViewType This indicates the type of view. A *TEXT view contains text retrieved from source files residing on the iSeries
server. The text contains sequence information from these files that the debugger may not want to display. A
*LISTING view contains text that is stored with the program object itself. A *STATEMENT view contains
information about HLL statements in the module, and this information is not generally displayed to the user but
is used by the debugger. In the case of this debugger, all views are displayed exactly as the text for the views are
retrieved.
CompilerID This indicates the language that the particular module is written in. This is not used by the example debugger.
MainIndicator Only one module in a program is the module with the program entry procedure (main() in the case of ILE C
programs). If a particular view in the list comes from this module, then this field indicates that the module
contains this procedure. This field is not used by the example debugger.
TimeStamp This indicates when the view was created. This is useful in allowing the debugger to detect if a program has been
recompiled and the debugger has down-level view information. This field is not used by the example debugger.
ViewDescription This is text given to the view by the compiler creating the view. It is a description of the view which can be
displayed by the debugger.
ViewNumber This is a sequence number of the view in a particular module. When registering a view, the program name,
module name, and view number must be provided.
NumViews This is how many views are in the module. All elements for views in a given module have the same value for this
field. This field is not used by the example debugger.
A loop through all the views returned by QteRetrieveModuleViews is done, registering the view using the QteRegisterDebugView
API. The program name, program type, module name, and view number of the module are passed as inputs to the API. The API
returns the library of the program (in case *LIBL) is passed in as the program library), the timestamp of the view (in case the program
has been recompiled between the time the view information was retrieved and the time the view was registered), the number of lines
of text in the view, and a view ID. The view ID is a handle, and it is used in identifying the registered view to various APIs. For
example, when retrieving text for a particular view, the view must be registered, and the view ID returned when registering the view
is passed to the QteRetrieveViewText API.
The structure that held the views retrieved by QteRetrieveModuleViews is also used by the debugger. The view number is no longer
needed, since it is just a sequence number passed to QteRegisterDebugView. Thus, this number is overwritten and will hold the view
ID, which is needed by other debugger APIs.
ProcessCommands()
void ProcessCommands(void) {
char InputBuffer[80];
char *token;
int i;
int step=0; /* do an exit for step when 1 */
step = ProcessDbgCommand(InputBuffer);
}
}
}
This function reads an input line from the user and processes it. If it is a command recognized by the debugger, it process it. If not, it
calls ProcessDebugCommand which lets QteSubmitDebugCommand process the command.
The first test is to make sure that the pointer to the debug data is not null. This is here for safety reasons. If program DEBUG is
compiled with the wrong activation group name or no name at all, its global variables can be destroyed when the program exits,
causing problems when the program is called again. This test prevents debug commands from being entered if the activation group
has been destroyed, wiping out the global view data.
The function loops until the quit command is entered or until a step is done. It calls the appropriate function based on the command
entered, or displays an error message if a syntax error is detected. If the command is unknown, it is processed by
ProcessDbgCommand.
The switch command is processed directly by the function. It changes the current view to a number provided. There is no error
checking in this sample debugger.
ReadLine()
This function reads a line of text from the user and fills the input buffer with trailing blanks.
ProcessListCommand()
void ProcessListCommand(void) {
char *token; /* pointer to next token of input*/
PrintViews
void PrintViews(void) {
int k;
/* loop through views printing view#, module, and view desc. text */
for (k=0; k< pgm_dbg_dta->NumberElements; k++) {
printf("%d) %.10s:%.50s",
k,
pgm_dbg_dta->Element[k].ModuleName,
pgm_dbg_dta->Element[k].ViewDescription);
if (current_view == k) /* indicate if view is current */
printf("<---Current\n");
else
printf("\n");
}
}
This routine lists all of the views available in the program being debugged. The information about the views is stored in the buffer
that was passed to QteRetrieveModuleViews.
The module name and view descriptive text is printed for each view. If the current view being printed is also the current view, this is
noted by printing this fact next to the view information.
A view number is printed next to each view. This is not the view ID returned by the QteRegisterDebugView. It is a number allowing
the user to change the current view to one of the views in the list.
PrintText()
void PrintText(void) {
This function retrieves the text associated with the current view and prints it. This text is the source of the program and is the heart of
a source debugger screen.
The text of the current view is retrieved, so the view ID of that view is determined. It is this view that is passed to
QteRetrieveViewText.
In the sample debugger, a large buffer is allocated, and as much text as will fit in this buffer is retrieved. The QteRetrieveViewText
API returns the text and the number of lines that fit in the buffer.
Once the text is retrieved, it is printed out along with the line number. The line number is needed when setting breakpoints based on
the view.
ProcessDbgCommand()
view_ID = pgm_dbg_dta->Element[current_view].ViewNumber;
CompilerID = &pgm_dbg_dta->Element[current_view].CompilerID;
if (errorCode.BytesAvailable != 0) {
printf("Error = %.7s\n",errorCode.ExceptionID);
return return_value;
}
This function is called to process all commands not known by the debugger. It calls the QteSubmitDebugCommand API which is
passed a view ID, compiler ID, and a command. The API needs the compiler ID because each programming language used in
compiling a particular module has different debug commands or command syntax, and the API needs to know which language was
used when compiling the module.
The API returns back a series of result records which indicate what was done by the API. Most of this function reads the results of the
records returned and prints an appropriate response message.
Some results records indicate that a particular function has been performed. These include:
_TE_kStepR The step command was successfully done.
_TE_kBreakR The break command was successfully done.
_TE_kQualifyR The qual command was successfully done.
_TE_kClearBreakpointR The clear breakpoint command was successfully done.
_TE_kClearPgmR The clear pgm command was successfully done.
Still other results records contain string data. In this case, the record contains an offset into the string space returned by the API as
well as a string length.
_TE_kExpressionTextR This points to the expression entered in the eval command.
_TE_kExpressionValueR This points to the value of the evaluated expression.
There are other kinds of results records than processed by the sample debugger. The QteSubmitDebugCommand API discusses in
detail each result record and the data it contains.
The API description also discusses the syntax of the debug command that must be passed to it. The commands and their syntax will
not be discussed in depth here, but a few example commands will be shown:
● break 5 when x == 3
This is a conditional breakpoint. The debugger will stop the program indicated by the view ID passed to the API when it
reaches line 5 of the view and when the expression "x == 3" is true. The "when" part of the break statement is optional, in
which case an unconditional breakpoint is set.
● step 1 into
The step command instructs the debug support to stop the a program when it has executed one or more statements. In this
example, the program is stopped after 1 statement has been executed. The "into" means that statements in procedures are
counted when stepping. "over" means that statements in called procedures are skipped over and not counted. The default step
type is "into", and the default step count is 1.
● qual 13
The qual command is necessary when there are blocks of code with the same variable name. In this case, the user indicates
where the variable is searched for in the program. Normally, this command is not used.
● clear 8
A conditional or unconditional breakpoint is removed from line 8 of the view indicated by the view ID parameter.
HandleStop()
This function is called when program DEBUG is called as a Program Stop Handler. It is passed the name, library, and type of the
program stopped, the line number in the statement view where it has stopped, a count of line numbers stopped in, if the system cannot
determine exactly where the program has stopped (this is the case for optimized code), and an array of character flags indicating why
the program was stopped.
The first thing the function does is determine if the current view is set to the module where the program stopped. If not, then it needs
to be reset to the first view in the module where the program has stopped.
Next, the statement view ID for the module stopped needs to be determined. This is necessary because the stopped position is given
in terms of the statement view, and this position needs to be converted to a position in the current view.
The QteMapViewPosition API maps a position in the statement view to a statement in another view in that module. This allows the
debugger to determine the source line of the current view where the program has stopped, even though the program is only told the
line number in the statement view.
Finally, the character flags are checked to see why the program was stopped. Note that the program can be stopped for more than one
reason, so every flag is checked, and if it is on, a message for that flag is printed.
Finally, the ProcessCommands function is called, allowing the user to enter debug commands.
Other APIs
This section discusses other APIs not covered in this example debugger. Some or all of these APIs could be used in a real ILE
source-level debugger. All of them are used in the debugger shipped with OS/400.
QteRetrieveDebugAttributes
This API allows a debugger to retrieve information about the debug session. This includes the value of the Update Production Files,
set on the Start Debug command, as well as an indication of whether the job where the debugger is running is servicing and
debugging another job.
QteSetDebugAttributes
The only attribute that can be set is the value of the Update Production Files. This can also be accomplished using the Change Debug
(CHGDBG) CL command.
QteRemoveDebugView
Views that are registered can be removed from debug. This is desirable if a program is to be removed from debug so that it can be
recompiled and added again. It is not necessary to remove views from debug when ending the debug session, as QteEndSourceDebug
will do this automatically.
QteRetrieveStoppedPosition
This indicates if a program is currently stopped and on the stack, and whether this stopped position is anywhere in a given view. This
is useful whenever a source debugger is about to put up a source screen. If the program is stopped somewhere within the source to be
displayed, this can be indicated to the user.
This is necessary because a program can be stopped by other means than the debugger. For example, an ILE program could have put
up a command entry screen, and the debugger could be displayed from there. In this case, it is nice to indicate to the user that the
program being debugged is stopped.
QteAddBreakpoint
This and the following APIs are not really needed, as their function can be done with the QteSubmitDebugCommand. However, this
API is much faster, since a debug language command does not need to be parsed and interpreted. In cases where the debugger knows
the information without needing to specify a debug command to the API, these "shortcut" APIs should be used.
This API performs the same function as the break n debug language command.
QteRemoveBreakpoint
This API performs the same function as the clear n debug language command.
QteRemoveAllBreakpoints
This API performs the same function as the clear pgm debug language command.
QteStep
This API performs the same function as the step n into and step n over debug language commands.
Debugger Code--Sample
Following is the entire program listing for the ILE C program containing the example debugger discussed in the preceding sections.
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: The entire program listing for the program */
/* containing the example debugger discussed in the */
/* preceding sections. */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: QteRetrieveViewText, QteSubmitDebugCommand, */
/* QteEndSourceDebug, QteRetrieveModuleViews, */
/* QteRegisterDebugView, QteStartSourceDebug, */
/* QteMapViewPosition */
/* */
/*******************************************************************/
/*******************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <qtedbgs.h>
/* PrintText: This function will print the text for the current view */
void PrintText(void) {
long LineLength = 92; /* length of lines of text */
long NumberOfLines = 0; /* lines to retrieve - 0 = all */
long StartLine=1; /* retrieve from line 1 (first) */
long bufferLength = 100000; /* size of retrieved text buffer */
long viewID; /* view ID of text to retrieve */
_TE_TEXT_BUFFER_T *buffer; /* text retrieved by API */
_TE_ERROR_CODE_T errorCode = {0}; /* Exceptions will be signaled */
int i; /* points to start of each line */
int line_number; /* line number counter for loop */
/* loop through views printing view#, module, and view desc. text */
for (k=0; k< pgm_dbg_dta->NumberElements; k++) {
printf("%d) %.10s:%.50s",
k,
pgm_dbg_dta->Element[k].ModuleName,
pgm_dbg_dta->Element[k].ViewDescription);
if (current_view == k) /* indicate if view is current */
printf("<---Current\n");
else
printf("\n");
}
}
view_ID = pgm_dbg_dta->Element[current_view].ViewNumber;
CompilerID = &pgm_dbg_dta->Element[current_view].CompilerID;
if (errorCode.BytesAvailable != 0) {
printf("Error = %.7s\n",errorCode.ExceptionID);
return return_value;
}
step = ProcessDbgCommand(InputBuffer);
}
}
}
Using the Spawn Process and Wait for Child Process APIs
The following two examples illustrate programs that use a parent/child relationship.
See the QlgSpawn--Spawn Process (using NLS-enabled path name) API for an example of supplying parameters in any CCSID.
Parent Program
This program acts as a parent to a child program (see Child Program).
This program demonstrates the use of the spawn() function and the wait() and waitpid() functions in a parent/child relationship. The
use of file descriptors, the creation of a new process group, arguments passed from parent to child, and environment variables are
demonstrated. The parent program uses spawn() in three different ways.
Use the Create C Module (CRTCMOD) and the Create Program (CRTPGM) commands to create this program (see Creating the
Parent and Child Program).
Call this program with no parameters (see Calling the Parent Program).
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: This program acts as a parent to a child program. */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: putenv(), spawn(), wait(), waitpid() */
/* */
/*******************************************************************/
/*******************************************************************/
#include <errno.h>
#include <fcntl.h>
#include <spawn.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAP_NUM 5
#define ARGV_NUM 6
#define ENVP_NUM 2
#define CHILD_PGM "QGPL/CHILD"
memset(&spw_inherit,0x00,sizeof(spw_inherit));
/* Set the spawn() child arguments that are common for each */
/* child. */
/* NOTE: The child will always get argv[0] in the */
/* LIBRARY/PROGRAM notation, but the */
/* spawn() argv[0] (spw_argv[0] */
/* in this case) must be non-NULL in order to allow additional */
/* arguments. For this example, the character pointer spw_path */
/* was chosen. */
/* NOTE: The parent pid and the parent process group are passed */
/* to the child for demonstration purposes only. */
spw_argv[0] = spw_path;
spw_argv[1] = pid_str;
spw_argv[2] = pgrp_str;
spw_argv[4] = fd_str;
spw_argv[5] = NULL;
/* The 2nd spawn() will use mapping for the file descriptor, */
/* along with the inheritance option to create a new process */
/* group for the child. */
spw_fd_count = 1;
spw_fd_map[0] = fd;
spw_inherit.pgroup = SPAWN_NEWPGROUP;
seq_num = 2;
sprintf(seq_num_str, "%d", seq_num);
spw_argv[3] = seq_num_str;
spw_envp[0] = NULL;
spw_child_pid[1] = spawn(spw_path, spw_fd_count, spw_fd_map,
&spw_inherit, spw_argv, spw_envp);
if (spw_child_pid[1] == -1)
{
printf("FAILURE: spawn() #2 with errno = %d\n",errno);
close(fd);
unlink(f_path_name);
return -1;
}
/* The 3rd spawn() will use mapping for the file descriptors */
/* with some file descriptors designated as being closed */
/* (SPAWN_FDCLOSED) and the same parent file descriptor mapped */
/* to more than one child file descriptor. In addition, an */
/* environment variable will be set and used by the child. */
spw_fd_count = 5;
spw_fd_map[0] = SPAWN_FDCLOSED;
spw_fd_map[1] = SPAWN_FDCLOSED;
spw_fd_map[2] = fd;
spw_fd_map[3] = SPAWN_FDCLOSED;
spw_fd_map[4] = fd;
spw_inherit.pgroup = 0;
seq_num = 3;
sprintf(seq_num_str, "%d", seq_num);
spw_argv[3] = seq_num_str;
strcpy(env_return_val,"return_val=3");
rc = putenv(env_return_val);
if (rc < 0)
{
printf("FAILURE: putenv() with errno = %d\n",errno);
close(fd);
unlink(f_path_name);
return -1;
}
spw_child_pid[2] = spawn(spw_path, spw_fd_count, spw_fd_map,
&spw_inherit, spw_argv, environ);
if (spw_child_pid[2] == -1)
{
printf("FAILURE: spawn() #3 with errno = %d\n",errno);
close(fd);
unlink(f_path_name);
return -1;
}
/* Open the file for read to verify what the child wrote. */
fd_read = open(f_path_name, O_RDONLY);
if (fd_read == -1)
{
printf("FAILURE: open() for read with errno = %d\n",errno);
unlink(f_path_name);
return -1;
}
Child Program
This program acts as a child to a parent program (see Parent Program). This program demonstrates how a child program uses
characteristics expressed through the use of spawn() in the parent program. The use of file descriptors, the creation of a new process
group, arguments passed from the parent, and environment variables are demonstrated. The child program handles three distinct calls
through the use of one of its arguments.
Use the CRTCMOD and CRTPGM commands to create this program (see Creating the Parent and Child Program).
This program is called by the spawn() function from the parent program. The program name must be CHILD and must be created into
library QGPL, as indicated by the parent program. This program is not to be called directly.
/*******************************************************************/
/*******************************************************************/
/* */
/* FUNCTION: This program acts as a child to a parent program. */
/* */
/* LANGUAGE: ILE C for OS/400 */
/* */
/* APIs USED: getenv(), getpid(), getppid(), getpgrp() */
/* */
/*******************************************************************/
/*******************************************************************/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
CRTCMOD MODULE(QGPL/PARENT)
SRCFILE(QGPL/QCSRC)
SRCMBR(PARENT)
TEXT('Example Parent')
CRTCMOD MODULE(QGPL/CHILD)
SRCFILE(QGPL/QCSRC)
SRCMBR(CHILD)
TEXT('Example Child')
CRTPGM PGM(QGPL/PARENT)
CRTPGM PGM(QGPL/CHILD)
CALL PGM(QGPL/PARENT)
Working with Stream Files
The following ILE C program performs the following functions:
● Opens an existing stream file
● Reads from the stream file and writes to the database file until end-of-file
The program uses the following hierarchical file system (HFS) APIs:
● Create Directory (QHFCRTDR)
/********************************************************************/
/* Program Name: HFSCOPY */
/* Language : ILE C for OS/400 */
/* Description : This program will do the following: */
/* -- Create or replace a stream file */
/* -- Create or replace a database file */
/* -- Read from the stream file and write to the */
/* database file until EOF */
/* -- Close both files when done */
/* APIs Used : QHFCRTDR, QHFOPNSF, QHFRDSF, QHFCLOSF */
/********************************************************************/
/********************************************************************/
/* Include files */
/********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <qhfopnsf.h>
#include <qhfrdsf.h>
#include <qhfclosf.h>
#include <qhfcrtdr.h>
#include <qusec.h>
/********************************************************************/
/* Structure and variable definitions */
/********************************************************************/
#define ON 1
#define OFF 0
typedef struct error_code_struct {
Qus_EC_t EC;
char exception_data[256];
}error_code_struct;
error_code_struct error_code;
char file_handle[16];
char path_name[30];
char open_info[10];
char attrib_info;
char action;
char read_buffer[80];
int path_length;
int attrib_length = 0;
int bytes_to_read;
int bytes_read = 0;
int end_file;
int cmpgood;
FILE *FP;
/*********************************************************************/
/*printErrCode: Routine to print the error code structure */
/*********************************************************************/
void printErrCode(error_code_struct *theErrCode)
{
int i;
char *tempptr = theErrCode->EC.Exception_Id;
printf("Bytes Provided -> %d\n",theErrCode->EC.Bytes_Provided);
printf("Bytes Available -> %d\n",theErrCode->EC.Bytes_Available);
printf("Exception ID -> ");
for (i=0;i<7 ;i++,tempptr++ )
{
putchar(*tempptr);
}
putchar('\n');
}
/********************************************************************/
/* Start of code */
/********************************************************************/
main()
{
error_code.EC.Bytes_Provided = 116;
/********************************************************************/
/* Create the directory */
/********************************************************************/
strcpy(path_name,"/QDLS/HFSFLR");
path_length = strlen(path_name);
QHFCRTDR(path_name,path_length,&attrib_info,attrib_length,&error_code);
if ( error_code.EC.Bytes_Available != 0 )
{
if (!memcmp(error_code.EC.Exception_Id,"CPF1F04",7))
printf("Directory HFSFLR already created.\n");
else
{
printErrCode(&error_code);
exit(1);
}
}
/********************************************************************/
/* Open the stream file */
/********************************************************************/
strcpy(open_info,"210 120 "); /* Create or replace the file */
strcpy(path_name,"/QDLS/HFSFLR/SAMPLE.HFS");
path_length = strlen(path_name);
printf("OPEN STREAM FILE:\n ");
QHFOPNSF(&file_handle,
path_name,
path_length,
open_info,
&attrib_info,
attrib_length,
&action,
&error_code);
if (error_code.EC.Bytes_Available != 0)
{
printErrCode(&error_code);
exit(1);
}
/********************************************************************/
/* Open a database file */
/********************************************************************/
system("CRTLIB LIB(HFSLIB)");
if (( FP = fopen("HFSLIB/HFSFILE(SAMPLE)","wb")) == NULL)
{
printf("Cannot open HFSLIB/HFSFILE(SAMPLE)\n");
exit(1);
}
/********************************************************************/
/* Loop through reading from the stream file and writing to the */
/* database file. */
/********************************************************************/
end_file = OFF;
while (end_file == OFF)
{
/*************************************************************/
/* Read 80 bytes from the stream file */
/*************************************************************/
bytes_to_read = 80;
printf("READ STREAM FILE:\n ");
QHFRDSF(&file_handle,
read_buffer,
bytes_to_read,
&bytes_read,
&error_code);
if (error_code.EC.Bytes_Available != 0)
{
cmpgood = strncmp("CPF1F33",error_code.EC.Exception_Id,7);
if (cmpgood != 0)
printErrCode(&error_code);
end_file = ON;
}
else
{
printf("BYTES READ: %d\n ",bytes_read);
printf("READ BUFFER: %s\n",read_buffer);
if (bytes_read < bytes_to_read)
{
end_file = ON;
/*******************************************************/
/* Write remaining bytes to the database file */
/*******************************************************/
if (bytes_read > 0)
fwrite(read_buffer,1,bytes_read,FP);
}
}
}
/********************************************************************/
/* Close the stream file */
/********************************************************************/
printf("CLOSE STREAM FILE:\n ");
QHFCLOSF(&file_handle,
&error_code);
if (error_code.EC.Bytes_Available != 0)
printErrCode(&error_code);
/********************************************************************/
/* Close the database file */
/********************************************************************/
fclose(FP);
}
/***************************************************************/
/***************************************************************/
/* */
/* FUNCTION: User-written exit program for doing Operational */
/* Assistant backup. */
/* */
/* LANGUAGE: CL */
/* */
/* APIs USED: None */
/* */
/***************************************************************/
/***************************************************************/
Note: This example does not show the apply-temporary to apply-permanent or the not-applied to remove-permanent cases. It is
assumed that all action was taken on the moves from loaded to apply-temporary and from apply-temporary to not-applied. If
additional actions are necessary, code could be added to handle those transitions as well.
Do not assume the default values for parameters on CL commands or for library lists. Users can change these values. Library lists can
vary from one system to another.
/*----------------------------------------------------------------*/
/* THE CURRENT STATUS OF THE PTF IS "LOADED (NOT APPLIED)" */
/*----------------------------------------------------------------*/
IF (&STATUS = '0') THEN(DO) /* If PTF is loaded but not applied */
IF (&ACTION = '1') THEN(DO) /* If action is temporarily */
/* applied then */
/*?---- TEMP APPLY - ADD YOUR STATEMENTS HERE ----- */
ENDDO
IF (&ACTION = '5') THEN(DO) /* If action will be temporarily */
/* apply then */
/*?---- PRE-TEMP APPLY - ADD YOUR STATEMENTS HERE ----- */
ENDDO
ENDDO /* End of loading the PTF */
/*----------------------------------------------------------------*/
/* THE CURRENT STATUS OF THE PTF IS "APPLIED TEMPORARILY" */
/*----------------------------------------------------------------*/
IF (&STATUS = '1') THEN(DO) /* If PTF is temporarily */
/* applied then */
IF (&ACTION = '0') THEN(DO) /* If action is temporarily */
/* removed then */
/*?---- TEMPORARILY REMOVE - ADD YOUR STATEMENTS HERE --- */
ENDDO
IF (&ACTION = '4') THEN(DO) /* If action will be temporarily */
/* remove then */
/*?---- PRE-TEMP REMOVE - ADD YOUR STATEMENTS HERE ----- */
ENDDO
ENDDO /* End of remove the PTF */
/*---------------------------------------------------------------*/
/* PTF HAS BEEN SUCCESSFULLY PROCESSED */
/*---------------------------------------------------------------*/
QSYS/SNDPGMMSG MSGID(CPC1214) MSGF(*LIBL/QCPFMSG) +
MSGDTA(*NONE) TOPGMQ(*PRV (* *NONE +
*NONE)) TOMSGQ(*TOPGMQ) MSGTYPE(*COMP)
RETURN
/*----------------------------------------------------------------*/
/* HANDLE ALL ERROR CONDITIONS */
/*----------------------------------------------------------------*/
HDLERR:
/* Try to back out any changes already made */
/* If nothing to back out or back-out operation was successful */
QSYS/SNDPGMMSG MSGID(CPF3638) MSGF(*LIBL/QCPFMSG) +
MSGDTA(*NONE) TOPGMQ(*PRV (* *NONE +
*NONE)) TOMSGQ(*TOPGMQ) MSGTYPE(*ESCAPE)
/* Else the permanent changes not backed out */
QSYS/SNDPGMMSG MSGID(CPF3639) MSGF(*LIBL/QCPFMSG) +
MSGDTA(*NONE) TOPGMQ(*PRV (* *NONE +
*NONE)) TOMSGQ(*TOPGMQ) MSGTYPE(*ESCAPE)