Sample C user exit
/*********************************************************************
* I D E N T I F I C A T I O N AND D E S C R I P T I O N *
**********************************************************************
* *
* PGM: ADUEXIC *
* *
* Description: Example BMC UNLOAD user exit *
* *
* Please review documentation at the end of this member *
* *
* Note that all non-standard C headers are included at the front of *
* this code. *
* *
*********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*-----------------------------------------------------*/
/* #include 'aduxith0.h' prototypes and #defines */
/*-----------------------------------------------------*/
#define ADU_MVS
/*
* defines
*/
#ifdef ADU_MSC
#define __asm
#define _BYREF
#else
#define _BYREF @
#endif
/*
* prototypes
*/
__asm void aduxitr1(int,struct LVAREA*);
/*--------------------------------------------*/
/* #include 'aduxith1.h' aduexitp */
/*--------------------------------------------*/
#if !defined(_XL2)
#define _XL2
typedef struct
{
#ifdef ADU_MVS
unsigned BF : 16;
#else
unsigned BF1 : 8;
unsigned BF2 : 8;
#endif
} XL2;
#endif
#if !defined(_CL100)
#define _CL100
typedef char CL100(|100|);
#endif
struct ADUEXITP
{
/*********************************************************************/
/* ADUEXITP DEFINES THE UNLOAD USER EXIT PARM BLOCK */
/* YOU MAY NOT MODIFY THE FIELDS IN FRONT OF THE USER AREA */
/* (ACTUALLY YOU MAY, BUT YOU WON'T LIKE WHAT HAPPENS) */
/*********************************************************************/
int xpfunc; /* 0 = PROCESS, 1 = INIT, 2=TERMINATE */
void *xpsqldaA_; /* A(SQLDA) FOR THIS TABLE */
void *xptableA_; /* A(TABLE NAME BEING UNLOADED) */
/* THE ABOVE POINTS TO 128 BYTE CREATOR, FOLLOWED BY 128 BYTE NAME*/
char xpflags; /* VARIOUS FLAGS */
#define XPFDEBUG 0x01 /* DEBUG IS ON */
char _f0; /* */
short xprefP_; /* BLOCK REFERENCE # */
void *xpssidA_; /* A(SSID) 4 BYTES*/
void *xpuserA_; /* A(USERID) 8 BYTES*/
void *xputidA_; /* A(UTILITY ID) 16 BYTES*/
int _f1(|6|); /* */
/* */
/* * USER AREA */
/* */
/*XPUSRMSG CONTAINS A SINGLE MESSAGE ENTRY. ON RETURN FROM THE EXIT,*/
/* IF THIS FIELD IS NON-BLANK, IT IS PRINTED THEN BLANKED.*/
/* */
/* XPUSRMS@ POINTS TO A MESSAGE BUFFER THAT CAN CONTAIN MULTIPLE EXIT*/
/* MESSAGES OF EQUAL LENGTH. THESE MESSAGE(S) WILL BE*/
/* PRINTED AFTER XPUSRMSG (IF ANY). */
/* FORMAT OF THE MESSAGE BUFFER AT THIS ADDRESS IS:*/
/* */
/* #MSGS DS H NUMBER OF LINES, 0 = NO PRINT*/
/* MSGSIZE DS H SIZE OF EACH LINE (MAX = 100)*/
/* MSGTEXT DS CL(#MSGS*MSGSIZE) USER MESSAGES*/
/* */
void *xpuserwA_; /* USER WORK AREA ADDRESS */
int xpuserf1; /* USER FIELD */
int xpuserf2; /* USER FIELD */
int xpuserf3; /* USER FIELD */
int xpuserf4; /* USER FIELD */
void *xpusermA_; /* USER MESSAGE BUFFER ADDRESS */
int xpusermz; /* USER MESSAGE BUFFER TOTAL SIZE */
CL100 xpusrmsg; /* USER SINGLE MESSAGE AREA */
/* */
XL2 xpresrv; /* RESERVED V12*/
/* */
char xpusrpad(|842|); /* PADDING */
#define XPK 1024 /* */
};
#define XPFLAGS xpflags
#define XPFUNC xpfunc
#define XPREFP_ xprefP_
#define XPRESRV xpresrv
#define XPSQLDAA_ xpsqldaA_
#define XPSSIDA_ xpssidA_
#define XPTABLEA_ xptableA_
#define XPUSERA_ xpuserA_
#define XPUSERF1 xpuserf1
#define XPUSERF2 xpuserf2
#define XPUSERF3 xpuserf3
#define XPUSERF4 xpuserf4
#define xpusermz xpusermz
#define XPUSERMA_ xpusermA_
#define XPUSERWA_ xpuserwA_
#define XPUSRMSG xpusrmsg
#define XPUSRPAD xpusrpad
#define XPUTIDA_ xputidA_
/*
SYMBOL OFFSET SIZE TYPE C-TYPE C-NAME
ADUEXITP 000000 000000
XPFLAGS 00000C 000001 X char xpflags
XPFUNC 000000 000004 F int xpfunc
XPREF# 00000E 000002 H short xprefP_
XPRESRV 0000B4 000002 XL2 XL2 xpresrv
XPSQLDA@ 000004 000004 A void * xpsqldaA_
XPSSID@ 000010 000004 A void * xpssidA_
XPTABLE@ 000008 000004 A void * xptableA_
XPUSER@ 000014 000004 A void * xpuserA_
XPUSERF1 000038 000004 F int xpuserf1
XPUSERF2 00003C 000004 F int xpuserf2
XPUSERF3 000040 000004 F int xpuserf3
XPUSERF4 000044 000004 F int xpuserf4
XPUSERMZ 00004C 000004 F int xpusermz
XPUSERM@ 000048 000004 A void * xpusermA_
XPUSERW@ 000034 000004 A void * xpuserwA_
XPUSRMSG 000050 000064 CL100 CL100 xpusrmsg
XPUSRPAD 0000B6 00034A (1024-... char (|842|) xpusrpad
XPUTID@ 000018 000004 A void * xputidA_
*/
/*-----------------------------------------*/
/* #include 'aduxith2.h' lvarea */
/*-----------------------------------------*/
#if !defined(_XL292)
#define _XL292
typedef char XL292(|292|);
#endif
#if !defined(_CL128)
#define _CL128
typedef char CL128(|128|);
#endif
#if !defined(_CL8)
#define _CL8
typedef char CL8(|8|);
#endif
#if !defined(_CL6)
#define _CL6
typedef char CL6(|6|);
#endif
#if !defined(_PL2)
#define _PL2
typedef struct
{
#ifdef ADU_MVS
unsigned BF :16;
#else
unsigned BF1 :8;
unsigned BF2 :8;
#endif
} PL2;
#endif
struct LVAREA
{
/*********************************************************************/
/* LOCAL VARIABLES WORK AREA DSECT */
/*********************************************************************/
int lvsave(|18|); /* LOCAL SAVE AREA */
PL2 lvkeepP_; /* RECORDS KEPT */
PL2 lvdiscP_; /* RECORDS DISCARDED */
#define LVBLANKA 76 /* START OF BLANKING AREA */
CL6 lvkeep; /* UNPKED KEEPERS */
CL6 lvdisc; /* UNPKED DISCARDS */
CL128 lvtbcrea; /* TABLE CREATOR */
CL128 lvtbname; /* TABLE NAME */
#define LVBLANKZ 38 /* SIZE OF BLANKING AREA */
XL292 lvspares; /* */
#define LVAREAZ 406 /* */
};
#define LVDISC lvdisc
#define LVDISCP_ lvdiscP_
#define LVKEEP lvkeep
#define LVKEEPP_ lvkeepP_
#define LVSAVE lvsave
#define LVSPARES lvspares
#define LVTBCREA lvtbcrea
#define LVTBNAME lvtbname
/*
SYMBOL OFFSET SIZE TYPE C-TYPE C-NAME
LVAREA 000000 000000
LVDISC 000052 000006 CL6 CL6 lvdisc
LVDISC# 00004A 000002 PL2 PL2 lvdiscP_
LVKEEP 00004C 000006 CL6 CL6 lvkeep
LVKEEP# 000048 000002 PL2 PL2 lvkeepP_
LVSAVE 000000 000048 18F int (|18|) lvsave
LVSPARES 000072 000124 XL292 XL292 lvspares
LVTBCREA 000058 000008 CL128 CL128 lvtbcrea
LVTBNAME 000060 000012 CL128 CL128 lvtbname
*/
/*---------------------------------------*/
/* #include 'aduxith3.h' SQLDA Header */
/*---------------------------------------*/
#if !defined(_CL8)
#define _CL8
typedef char CL8(|8|);
#endif
struct SQLDA
{
CL8 sqldaid; /* ID */
int sqldabc; /* BYTE COUNT */
short sqln; /* TOTAL VARS */
short sqld; /* PERTINENT VARS */
int sqlvar(|0|); /* BEGIN VARS */
#define SQLDSIZ 16 /* SIZE OF FIXED PART */
};
#define SQLD sqld
#define SQLDABC sqldabc
#define SQLDAID sqldaid
#define SQLN sqln
#define SQLVAR sqlvar(|0|)
/*
SYMBOL OFFSET SIZE TYPE C-TYPE C-NAME
SQLD 00000E 000002 H short sqld
SQLDA 000000 000000
SQLDABC 000008 000004 F int sqldabc
SQLDAID 000000 000008 CL8 CL8 sqldaid
SQLN 00000C 000002 H short sqln
SQLVAR 000010 000000 0F int (|0|) sqlvar
*/
/*---------------------------------------------*/
/* #include 'aduxith4.h' SQLVARN HEADER START */
/*---------------------------------------------*/
#if !defined(_CL30)
#define _CL30
typedef char CL30(|30|);
#endif
struct SQLVARN
{
short sqltype; /* TYPE CODE */
#ifdef ADU_MVS
short sqllen(|0|); /* NAME LENGTH */
#else
short sqllen;
#endif
char sqlprcsn; /* DEC PRECISION */
char sqlscale; /* DEC SCALE */
void *sqldata; /* ADDR OF VAR */
void *sqlind; /* ADDR OF IND */
union
{
struct
{
short sqlname; /* DESCRIBE NAME */
CL30 _f0; /* */
} _s0;
struct
{
short sqlnamz; /* SIZEOF COLNAME*/
CL30 sqlnam; /* NAME ALONE */
#define SQLVSIZ 44 /* */
} _s1;
};
};
/*--------------------*/
/* end SQLVARN HEADER */
/*--------------------*/
#define SQLDATA sqldata
#define SQLIND sqlind
#define SQLLEN sqllen(|0|)
#define SQLNAM _s1.sqlnam
#define sqlnamz _s1.sqlnamz
#define SQLNAME _s0.sqlname
#define SQLPRCSN sqlprcsn
#define SQLSCALE sqlscale
#define SQLTYPE sqltype
/*
SYMBOL OFFSET SIZE TYPE C-TYPE C-NAME
SQLDATA 000004 000004 A void * sqldata
SQLIND 000008 000004 A void * sqlind
SQLLEN 000002 000000 0H short (|0|) sqllen
SQLNAM 00000E 00001E CL30 CL30 _s1.sqlnam
sqlnamz 00000C 000002 H short _s1.sqlnamz
SQLNAME 00000C 000002 H short _s0.sqlname
SQLPRCSN 000002 000001 X char sqlprcsn
SQLSCALE 000003 000001 X char sqlscale
SQLTYPE 000000 000002 H short sqltype
SQLVARN 000000 000000
*/
static int BadFuncCode(struct ADUEXITP *pzAduexitp);
#define MAXFUNC 2
#define PROCESS 0
#define INITIAL 1
#define CLEANUP 2
static char
szAccept (||) = 'ACCEPTED'
,szDiscard (||) = 'DISCARDED'
,szMbadfunc (||) = 'BAD FUNCTION CODE RECEIVED FROM MAIN'
,szMgetmerr (||) = 'GETMAIN ERROR GETTING USER STORAGE'
,szMgetmer2 (||) = 'GETMAIN ERROR GETTING MSG STORAGE'
,szIgot2ini (||) = 'COLUMNS AVAILABLE FROM TABLE'
,szIgot2pro (||) = 'PROCESSING REGISTRATION ID:'
,szIgot2cln (||) = 'PROCESSING IS COMPLETED'
;
int aduexic( long lFcode ,struct ADUEXITP *pzAduexitp ){
int lRc;
/*---------------------*
* Main Entry Point *
*---------------------*/
lRc = 0;
switch(lFcode){
case PROCESS:
lRc = Process(pzAduexitp);
break;
case INITIAL:
lRc = Initial(pzAduexitp);
break;
case CLEANUP:
lRc = Cleanup(pzAduexitp);
break;
default:
return BadFuncCode(pzAduexitp);
}
return lRc;
}
/*-------------------------------------------------------------------*
* *
* INTERNAL ROUTINES *
* INTERNAL . ROUTINES *
* INTERNAL ..... ROUTINES *
* INTERNAL . ROUTINES *
* INTERNAL ROUTINES *
* *
*-------------------------------------------------------------------*/
/*------------------------------------*
* handle bad function call *
*------------------------------------*/
static int BadFuncCode(struct ADUEXITP *pzAduexitp){
memset(pzAduexitp->XPUSRMSG ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
memcpy(pzAduexitp->XPUSRMSG ,szMbadfunc ,sizeof(szMbadfunc));
return 8;
}
/*------------------------------------*
* Processing call *
*------------------------------------*/
static int Process(struct ADUEXITP *pzAduexitp){
short sCols;
char *pstrMsg;
struct SQLDA *pzSqlda;
struct SQLVARN *pzSqlvarn;
if ( (pstrMsg=pzAduexitp->XPUSERMA_)!=NULL ){
*(short*)pstrMsg = 0;
}
/* Say I got here */
memcpy(pzAduexitp->XPUSRMSG ,szIgot2pro ,sizeof(szIgot2pro));
pzSqlda = pzAduexitp->XPSQLDAA_;
sCols = pzSqlda->SQLN;
pzSqlvarn = (struct SQLVARN*)( ((char*)pzSqlda)+SQLDSIZ);
while(sCols){
if ( memcmp(pzSqlvarn->SQLNAM ,'REGISTRATION' ,12)==0 ){
memcpy(pzAduexitp->XPUSRMSG+sizeof(szIgot2pro)
,pzSqlvarn->SQLDATA
,8);
if ( (memcmp(pzSqlvarn->SQLDATA ,'NCC-1700' ,8)>=0)
&& (memcmp(pzSqlvarn->SQLDATA ,'NCC-1711' ,8)<=0) ){
/* We're starting over with columns */
sCols = pzSqlda->SQLN;
pzSqlvarn = (struct SQLVARN*)( ((char*)pzSqlda)+SQLDSIZ);
while(sCols){
if (memcmp(pzSqlvarn->SQLNAM,'NAME ',5)==0){
if ( (pzSqlvarn->SQLIND == NULL)
|| ((pzSqlvarn->SQLIND != NULL)
&& (*((char*)pzSqlvarn->SQLIND)=='?')) ){
memcpy(pzAduexitp->XPUSRMSG
+sizeof(pzAduexitp->XPUSRMSG)-21
,'STARSHIP NAME IS NULL'
,21);
return 4; /* Q U I T ! */
} else {
aduxitr1(2,pzAduexitp->XPUSERWA_); /* bump keep count */
return 0; /* Q U I T ! */
}
}
pzSqlvarn++;
sCols--;
}
memcpy(pzAduexitp->XPUSRMSG ,'NAME COLUMN NOT FOUND' ,21);
return 8; /* Q U I T ! */
} else {
aduxitr1(1,pzAduexitp->XPUSERWA_); /* bump discard count */
memcpy(pzAduexitp->XPUSRMSG
+sizeof(pzAduexitp->XPUSRMSG)
-sizeof(szDiscard)
,szDiscard
,sizeof(szDiscard));
return 4; /* Q U I T ! */
}
}
pzSqlvarn++;
sCols--;
}
memcpy(pzAduexitp->XPUSRMSG ,'REGISTRATION COLUMN NOT FOUND' ,29);
return 8; /* Q U I T ! */
}
/*------------------------*
* Exit Initialization *
*------------------------*/
static int Initial(struct ADUEXITP *pzAduexitp){
short sCols, sMsgs;
char *pstrMsg;
struct LVAREA *pzLvarea;
struct SQLDA *pzSqlda;
struct SQLVARN *pzSqlvarn;
struct TBNAME {
char strCreator (|8|) ;
char strName (|18|) ;
} *pzTb;
if ( memcmp(pzAduexitp->XPUTIDA_ ,'JST' ,3) != 0){
return 4;
} else if (memcmp(((char*)pzAduexitp->XPTABLEA_)+8
,'STARSHIP' ,8) != 0){
return 4;
}
/* allocate user workarea */
if ( (pzLvarea=calloc(1,LVAREAZ))==NULL){
return GetmainError(1,pzAduexitp);
}
pzAduexitp->XPUSERWA_ = pzLvarea; /* remember for use later */
aduxitr1(0,pzLvarea);
memset(pzLvarea->LVKEEP ,' ' ,sizeof(LVBLANKZ));
pzTb = pzAduexitp->XPTABLEA_;
memcpy(pzLvarea->LVTBCREA ,pzTb->strCreator ,sizeof(pzLvarea->LVTBCREA));
memcpy(pzLvarea->LVTBNAME ,pzTb->strName ,sizeof(pzLvarea->LVTBNAME));
/* Say I am here, and have this table */
memcpy(pzAduexitp->XPUSRMSG ,szIgot2ini ,sizeof(szIgot2ini));
memcpy(pzAduexitp->XPUSRMSG+sizeof(szIgot2ini)
,pzAduexitp->XPTABLEA_
,sizeof(struct TBNAME));
pzSqlda = pzAduexitp->XPSQLDAA_;
sMsgs = pzSqlda->SQLN * sizeof(pzAduexitp->XPUSRMSG);
sMsgs += 4+256; /* adjust for prefix and pad */
pzAduexitp->xpusermz = (int)sMsgs;
if ( (pzAduexitp->XPUSERMA_=calloc(1,sMsgs))==NULL){
return GetmainError(2,pzAduexitp);
}
sCols = pzSqlda->SQLN;
pzSqlvarn = (struct SQLVARN*)( ((char*)pzSqlda)+SQLDSIZ);
pstrMsg = pzAduexitp->XPUSERMA_;
pstrMsg += 4;
sMsgs = 0;
while(sCols){
memset(pstrMsg ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
memcpy(pstrMsg ,pzSqlvarn->SQLNAM ,sizeof(pzSqlvarn->SQLNAM));
sMsgs++;
pstrMsg += sizeof(pzAduexitp->XPUSRMSG);
pzSqlvarn++;
sCols--;
}
/*
memcpy(pzAduexitp->XPUSERMA_ ,&sMsgs ,sizeof(sMsgs));
*/
*((short*)pzAduexitp->XPUSERMA_) = sMsgs;
*(((short*)pzAduexitp->XPUSERMA_)+1) = sizeof(pzAduexitp->XPUSRMSG);
return 0;
}
/*--------------------------------*
* Exit termination processing *
*--------------------------------*/
static int Cleanup(struct ADUEXITP *pzAduexitp){
short sMsgs;
char *pstrMsg;
struct LVAREA *pzLvarea;
pzLvarea = pzAduexitp->XPUSERWA_;
memcpy(pzAduexitp->XPUSRMSG ,szIgot2cln ,sizeof(szIgot2cln));
sMsgs = 0;
pstrMsg = pzAduexitp->XPUSERMA_;
pstrMsg += 4; /* bump past gunk */
aduxitr1(3,pzAduexitp->XPUSERWA_); /* unpack counts */
memset(pstrMsg ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
memcpy(pstrMsg ,pzLvarea->LVDISC ,sizeof(pzLvarea->LVDISC));
memcpy(pstrMsg+sizeof(pzLvarea->LVDISC)+1
,'DID NOT QUALIFY AS HEAVY CRUISERS'
,33);
pstrMsg += sizeof(pzAduexitp->XPUSRMSG);
sMsgs++;
memset(pstrMsg ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
memcpy(pstrMsg ,pzLvarea->LVKEEP ,sizeof(pzLvarea->LVKEEP));
memcpy(pstrMsg+sizeof(pzLvarea->LVKEEP)+1
,'QUALIFIED AS HEAVY CRUISERS'
,27);
pstrMsg += sizeof(pzAduexitp->XPUSRMSG);
sMsgs++;
memset(pstrMsg ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
memcpy(pstrMsg+sizeof(pzLvarea->LVKEEP)+1
,'** ALL HEAVY CRUISERS NOW UNLOADED **'
,37);
*((short*)pzAduexitp->XPUSERMA_) = sMsgs;
/* free message block if we acquired one */
if (pzAduexitp->XPUSERMA_ != NULL){
free(pzAduexitp->XPUSERMA_);
pzAduexitp->XPUSERMA_ = NULL;
}
/* free main workarea */
free(pzAduexitp->XPUSERWA_);
pzAduexitp->XPUSERWA_ = NULL;
return 0;
}
/*-------------------------------------*
* Error in acquiring local storage *
*-------------------------------------*/
static int GetmainError(int lType ,struct ADUEXITP *pzAduexitp){
memset(pzAduexitp->XPUSRMSG ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
if (lType == 1){
memcpy(pzAduexitp->XPUSRMSG ,szMgetmerr ,sizeof(szMgetmerr));
} else {
memcpy(pzAduexitp->XPUSRMSG ,szMgetmer2 ,sizeof(szMgetmer2));
}
return 8;
}
/*
***************************
* D I S C L A I M E R *
***************************
This is a sample UNLOAD PLUS user exit written in SAS C.
This exit would be used in order to inspect and/or manipulate unload
data records prior to their being written to the sort or output
dataset.
This exit will only be invoked when it is specifically named
in the 'UNLOADEXIT ADUEXIC C' parameter.
Note: please review the documentation in the reference manual,
and the following usage notes prior to implementing this exit.
please call BMC Product Support with any questions you may
have in this area.
Phone: 1-800-841-2031 outside Texas
1-713-240-8800 in Texas
*******************************
* E X I T S C E N A R I O *
*******************************
As always it is tough to come up with a good example for an exit
that is intended to be application oriented. For the purposes of
this example, a DB2 table exists containing data on Federation
starships.
The DB2 table name is STARSHIP, containing the following columns:
REGISTRATION CHAR(16) NOT NULL,
NAME VARCHAR(64))
The purpose of this exit is to screen the starships by registration
number to filter out any non-heavy cruiser class starships.
In the star fleet, there were 12 heavy cruiser class starships
with registration numbers between NCC-1700 and NCC-1711.
This code only allows these records to be unloaded.
(This filtering could be done with unload control statements, but
since this data would appear on a report that could be stolen by
the Romulans, starfleet feels it is more secure to hide the logic
for selection in a program)
In addition, this screening is only done if the unload utility id
begins with the characters 'JST'.
The name must also be checked to ensure that it is not null since
this is a nullable field (clerical errors do occur occaisionally!)
the bottom line of all this is that this example shows how to:
1. Inspect the user exit block and pseudo SQLDA
2. Getmain storage needed that is preserved during the unload
3. Getmain storage for multiple user messages
4. Issue single and multiple messages for DB2 UNLOAD PLUS to issue
5. Find and look at unload table column names
6. Find and inspect data prior to being unloaded
7. Determine null fields
8. Print records being processed with messages
9. Accept and discard records from UNLOAD PLUS processing
*****************
* N O T E S *
*****************
ADUEXIC is called at 3 points in processing a table unload.
When invoked, r0 contains a function code.
r1 contains the address of a user exit block described by the
ADUEXITP dsect copied to your program from macro member @aduxprm.
fields for your use in this block are described in the dsect.
The main field of interest is the pseudo-sqlda pointer xpsqlda@
which points to an 'sqlda' prepared for each selected unload table.
this sqlda has the same format and contents of a db2 sqlda, with the
main exception being that the descriptions of the data fields are
the output descriptions (after conversions if any), and the data
pointers are pointing to the output record offsets in the record
about to be written (function call 0 only).
function:
Function code 0 = process record
this call is made after a record is prepared for writing.
all fields are converted ready for output. the sqlda
provides the field types and record positions of the data.
return codes from process:
0 = accept this record
4 = discard this record
anything else = terminate the run.
Function code 1 = initialization call
this call is made during table unload initialization. it
happens once per table to be unloaded to allow selection of
whether or not to process the table with the exit.
return codes from initialize:
0 = activate exit for records from this table
4 = don't use the exit for this table.
anything else = terminate the run.
Function code 2 = termination/cleanup call
this call is made just prior to termination of the unload,
to allow you to perform any cleanup functions necessary.
return codes for terminate are ignored.
for any exit function, the exit may insert message text in the
message area for printing upon return.
*/
* I D E N T I F I C A T I O N AND D E S C R I P T I O N *
**********************************************************************
* *
* PGM: ADUEXIC *
* *
* Description: Example BMC UNLOAD user exit *
* *
* Please review documentation at the end of this member *
* *
* Note that all non-standard C headers are included at the front of *
* this code. *
* *
*********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*-----------------------------------------------------*/
/* #include 'aduxith0.h' prototypes and #defines */
/*-----------------------------------------------------*/
#define ADU_MVS
/*
* defines
*/
#ifdef ADU_MSC
#define __asm
#define _BYREF
#else
#define _BYREF @
#endif
/*
* prototypes
*/
__asm void aduxitr1(int,struct LVAREA*);
/*--------------------------------------------*/
/* #include 'aduxith1.h' aduexitp */
/*--------------------------------------------*/
#if !defined(_XL2)
#define _XL2
typedef struct
{
#ifdef ADU_MVS
unsigned BF : 16;
#else
unsigned BF1 : 8;
unsigned BF2 : 8;
#endif
} XL2;
#endif
#if !defined(_CL100)
#define _CL100
typedef char CL100(|100|);
#endif
struct ADUEXITP
{
/*********************************************************************/
/* ADUEXITP DEFINES THE UNLOAD USER EXIT PARM BLOCK */
/* YOU MAY NOT MODIFY THE FIELDS IN FRONT OF THE USER AREA */
/* (ACTUALLY YOU MAY, BUT YOU WON'T LIKE WHAT HAPPENS) */
/*********************************************************************/
int xpfunc; /* 0 = PROCESS, 1 = INIT, 2=TERMINATE */
void *xpsqldaA_; /* A(SQLDA) FOR THIS TABLE */
void *xptableA_; /* A(TABLE NAME BEING UNLOADED) */
/* THE ABOVE POINTS TO 128 BYTE CREATOR, FOLLOWED BY 128 BYTE NAME*/
char xpflags; /* VARIOUS FLAGS */
#define XPFDEBUG 0x01 /* DEBUG IS ON */
char _f0; /* */
short xprefP_; /* BLOCK REFERENCE # */
void *xpssidA_; /* A(SSID) 4 BYTES*/
void *xpuserA_; /* A(USERID) 8 BYTES*/
void *xputidA_; /* A(UTILITY ID) 16 BYTES*/
int _f1(|6|); /* */
/* */
/* * USER AREA */
/* */
/*XPUSRMSG CONTAINS A SINGLE MESSAGE ENTRY. ON RETURN FROM THE EXIT,*/
/* IF THIS FIELD IS NON-BLANK, IT IS PRINTED THEN BLANKED.*/
/* */
/* XPUSRMS@ POINTS TO A MESSAGE BUFFER THAT CAN CONTAIN MULTIPLE EXIT*/
/* MESSAGES OF EQUAL LENGTH. THESE MESSAGE(S) WILL BE*/
/* PRINTED AFTER XPUSRMSG (IF ANY). */
/* FORMAT OF THE MESSAGE BUFFER AT THIS ADDRESS IS:*/
/* */
/* #MSGS DS H NUMBER OF LINES, 0 = NO PRINT*/
/* MSGSIZE DS H SIZE OF EACH LINE (MAX = 100)*/
/* MSGTEXT DS CL(#MSGS*MSGSIZE) USER MESSAGES*/
/* */
void *xpuserwA_; /* USER WORK AREA ADDRESS */
int xpuserf1; /* USER FIELD */
int xpuserf2; /* USER FIELD */
int xpuserf3; /* USER FIELD */
int xpuserf4; /* USER FIELD */
void *xpusermA_; /* USER MESSAGE BUFFER ADDRESS */
int xpusermz; /* USER MESSAGE BUFFER TOTAL SIZE */
CL100 xpusrmsg; /* USER SINGLE MESSAGE AREA */
/* */
XL2 xpresrv; /* RESERVED V12*/
/* */
char xpusrpad(|842|); /* PADDING */
#define XPK 1024 /* */
};
#define XPFLAGS xpflags
#define XPFUNC xpfunc
#define XPREFP_ xprefP_
#define XPRESRV xpresrv
#define XPSQLDAA_ xpsqldaA_
#define XPSSIDA_ xpssidA_
#define XPTABLEA_ xptableA_
#define XPUSERA_ xpuserA_
#define XPUSERF1 xpuserf1
#define XPUSERF2 xpuserf2
#define XPUSERF3 xpuserf3
#define XPUSERF4 xpuserf4
#define xpusermz xpusermz
#define XPUSERMA_ xpusermA_
#define XPUSERWA_ xpuserwA_
#define XPUSRMSG xpusrmsg
#define XPUSRPAD xpusrpad
#define XPUTIDA_ xputidA_
/*
SYMBOL OFFSET SIZE TYPE C-TYPE C-NAME
ADUEXITP 000000 000000
XPFLAGS 00000C 000001 X char xpflags
XPFUNC 000000 000004 F int xpfunc
XPREF# 00000E 000002 H short xprefP_
XPRESRV 0000B4 000002 XL2 XL2 xpresrv
XPSQLDA@ 000004 000004 A void * xpsqldaA_
XPSSID@ 000010 000004 A void * xpssidA_
XPTABLE@ 000008 000004 A void * xptableA_
XPUSER@ 000014 000004 A void * xpuserA_
XPUSERF1 000038 000004 F int xpuserf1
XPUSERF2 00003C 000004 F int xpuserf2
XPUSERF3 000040 000004 F int xpuserf3
XPUSERF4 000044 000004 F int xpuserf4
XPUSERMZ 00004C 000004 F int xpusermz
XPUSERM@ 000048 000004 A void * xpusermA_
XPUSERW@ 000034 000004 A void * xpuserwA_
XPUSRMSG 000050 000064 CL100 CL100 xpusrmsg
XPUSRPAD 0000B6 00034A (1024-... char (|842|) xpusrpad
XPUTID@ 000018 000004 A void * xputidA_
*/
/*-----------------------------------------*/
/* #include 'aduxith2.h' lvarea */
/*-----------------------------------------*/
#if !defined(_XL292)
#define _XL292
typedef char XL292(|292|);
#endif
#if !defined(_CL128)
#define _CL128
typedef char CL128(|128|);
#endif
#if !defined(_CL8)
#define _CL8
typedef char CL8(|8|);
#endif
#if !defined(_CL6)
#define _CL6
typedef char CL6(|6|);
#endif
#if !defined(_PL2)
#define _PL2
typedef struct
{
#ifdef ADU_MVS
unsigned BF :16;
#else
unsigned BF1 :8;
unsigned BF2 :8;
#endif
} PL2;
#endif
struct LVAREA
{
/*********************************************************************/
/* LOCAL VARIABLES WORK AREA DSECT */
/*********************************************************************/
int lvsave(|18|); /* LOCAL SAVE AREA */
PL2 lvkeepP_; /* RECORDS KEPT */
PL2 lvdiscP_; /* RECORDS DISCARDED */
#define LVBLANKA 76 /* START OF BLANKING AREA */
CL6 lvkeep; /* UNPKED KEEPERS */
CL6 lvdisc; /* UNPKED DISCARDS */
CL128 lvtbcrea; /* TABLE CREATOR */
CL128 lvtbname; /* TABLE NAME */
#define LVBLANKZ 38 /* SIZE OF BLANKING AREA */
XL292 lvspares; /* */
#define LVAREAZ 406 /* */
};
#define LVDISC lvdisc
#define LVDISCP_ lvdiscP_
#define LVKEEP lvkeep
#define LVKEEPP_ lvkeepP_
#define LVSAVE lvsave
#define LVSPARES lvspares
#define LVTBCREA lvtbcrea
#define LVTBNAME lvtbname
/*
SYMBOL OFFSET SIZE TYPE C-TYPE C-NAME
LVAREA 000000 000000
LVDISC 000052 000006 CL6 CL6 lvdisc
LVDISC# 00004A 000002 PL2 PL2 lvdiscP_
LVKEEP 00004C 000006 CL6 CL6 lvkeep
LVKEEP# 000048 000002 PL2 PL2 lvkeepP_
LVSAVE 000000 000048 18F int (|18|) lvsave
LVSPARES 000072 000124 XL292 XL292 lvspares
LVTBCREA 000058 000008 CL128 CL128 lvtbcrea
LVTBNAME 000060 000012 CL128 CL128 lvtbname
*/
/*---------------------------------------*/
/* #include 'aduxith3.h' SQLDA Header */
/*---------------------------------------*/
#if !defined(_CL8)
#define _CL8
typedef char CL8(|8|);
#endif
struct SQLDA
{
CL8 sqldaid; /* ID */
int sqldabc; /* BYTE COUNT */
short sqln; /* TOTAL VARS */
short sqld; /* PERTINENT VARS */
int sqlvar(|0|); /* BEGIN VARS */
#define SQLDSIZ 16 /* SIZE OF FIXED PART */
};
#define SQLD sqld
#define SQLDABC sqldabc
#define SQLDAID sqldaid
#define SQLN sqln
#define SQLVAR sqlvar(|0|)
/*
SYMBOL OFFSET SIZE TYPE C-TYPE C-NAME
SQLD 00000E 000002 H short sqld
SQLDA 000000 000000
SQLDABC 000008 000004 F int sqldabc
SQLDAID 000000 000008 CL8 CL8 sqldaid
SQLN 00000C 000002 H short sqln
SQLVAR 000010 000000 0F int (|0|) sqlvar
*/
/*---------------------------------------------*/
/* #include 'aduxith4.h' SQLVARN HEADER START */
/*---------------------------------------------*/
#if !defined(_CL30)
#define _CL30
typedef char CL30(|30|);
#endif
struct SQLVARN
{
short sqltype; /* TYPE CODE */
#ifdef ADU_MVS
short sqllen(|0|); /* NAME LENGTH */
#else
short sqllen;
#endif
char sqlprcsn; /* DEC PRECISION */
char sqlscale; /* DEC SCALE */
void *sqldata; /* ADDR OF VAR */
void *sqlind; /* ADDR OF IND */
union
{
struct
{
short sqlname; /* DESCRIBE NAME */
CL30 _f0; /* */
} _s0;
struct
{
short sqlnamz; /* SIZEOF COLNAME*/
CL30 sqlnam; /* NAME ALONE */
#define SQLVSIZ 44 /* */
} _s1;
};
};
/*--------------------*/
/* end SQLVARN HEADER */
/*--------------------*/
#define SQLDATA sqldata
#define SQLIND sqlind
#define SQLLEN sqllen(|0|)
#define SQLNAM _s1.sqlnam
#define sqlnamz _s1.sqlnamz
#define SQLNAME _s0.sqlname
#define SQLPRCSN sqlprcsn
#define SQLSCALE sqlscale
#define SQLTYPE sqltype
/*
SYMBOL OFFSET SIZE TYPE C-TYPE C-NAME
SQLDATA 000004 000004 A void * sqldata
SQLIND 000008 000004 A void * sqlind
SQLLEN 000002 000000 0H short (|0|) sqllen
SQLNAM 00000E 00001E CL30 CL30 _s1.sqlnam
sqlnamz 00000C 000002 H short _s1.sqlnamz
SQLNAME 00000C 000002 H short _s0.sqlname
SQLPRCSN 000002 000001 X char sqlprcsn
SQLSCALE 000003 000001 X char sqlscale
SQLTYPE 000000 000002 H short sqltype
SQLVARN 000000 000000
*/
static int BadFuncCode(struct ADUEXITP *pzAduexitp);
#define MAXFUNC 2
#define PROCESS 0
#define INITIAL 1
#define CLEANUP 2
static char
szAccept (||) = 'ACCEPTED'
,szDiscard (||) = 'DISCARDED'
,szMbadfunc (||) = 'BAD FUNCTION CODE RECEIVED FROM MAIN'
,szMgetmerr (||) = 'GETMAIN ERROR GETTING USER STORAGE'
,szMgetmer2 (||) = 'GETMAIN ERROR GETTING MSG STORAGE'
,szIgot2ini (||) = 'COLUMNS AVAILABLE FROM TABLE'
,szIgot2pro (||) = 'PROCESSING REGISTRATION ID:'
,szIgot2cln (||) = 'PROCESSING IS COMPLETED'
;
int aduexic( long lFcode ,struct ADUEXITP *pzAduexitp ){
int lRc;
/*---------------------*
* Main Entry Point *
*---------------------*/
lRc = 0;
switch(lFcode){
case PROCESS:
lRc = Process(pzAduexitp);
break;
case INITIAL:
lRc = Initial(pzAduexitp);
break;
case CLEANUP:
lRc = Cleanup(pzAduexitp);
break;
default:
return BadFuncCode(pzAduexitp);
}
return lRc;
}
/*-------------------------------------------------------------------*
* *
* INTERNAL ROUTINES *
* INTERNAL . ROUTINES *
* INTERNAL ..... ROUTINES *
* INTERNAL . ROUTINES *
* INTERNAL ROUTINES *
* *
*-------------------------------------------------------------------*/
/*------------------------------------*
* handle bad function call *
*------------------------------------*/
static int BadFuncCode(struct ADUEXITP *pzAduexitp){
memset(pzAduexitp->XPUSRMSG ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
memcpy(pzAduexitp->XPUSRMSG ,szMbadfunc ,sizeof(szMbadfunc));
return 8;
}
/*------------------------------------*
* Processing call *
*------------------------------------*/
static int Process(struct ADUEXITP *pzAduexitp){
short sCols;
char *pstrMsg;
struct SQLDA *pzSqlda;
struct SQLVARN *pzSqlvarn;
if ( (pstrMsg=pzAduexitp->XPUSERMA_)!=NULL ){
*(short*)pstrMsg = 0;
}
/* Say I got here */
memcpy(pzAduexitp->XPUSRMSG ,szIgot2pro ,sizeof(szIgot2pro));
pzSqlda = pzAduexitp->XPSQLDAA_;
sCols = pzSqlda->SQLN;
pzSqlvarn = (struct SQLVARN*)( ((char*)pzSqlda)+SQLDSIZ);
while(sCols){
if ( memcmp(pzSqlvarn->SQLNAM ,'REGISTRATION' ,12)==0 ){
memcpy(pzAduexitp->XPUSRMSG+sizeof(szIgot2pro)
,pzSqlvarn->SQLDATA
,8);
if ( (memcmp(pzSqlvarn->SQLDATA ,'NCC-1700' ,8)>=0)
&& (memcmp(pzSqlvarn->SQLDATA ,'NCC-1711' ,8)<=0) ){
/* We're starting over with columns */
sCols = pzSqlda->SQLN;
pzSqlvarn = (struct SQLVARN*)( ((char*)pzSqlda)+SQLDSIZ);
while(sCols){
if (memcmp(pzSqlvarn->SQLNAM,'NAME ',5)==0){
if ( (pzSqlvarn->SQLIND == NULL)
|| ((pzSqlvarn->SQLIND != NULL)
&& (*((char*)pzSqlvarn->SQLIND)=='?')) ){
memcpy(pzAduexitp->XPUSRMSG
+sizeof(pzAduexitp->XPUSRMSG)-21
,'STARSHIP NAME IS NULL'
,21);
return 4; /* Q U I T ! */
} else {
aduxitr1(2,pzAduexitp->XPUSERWA_); /* bump keep count */
return 0; /* Q U I T ! */
}
}
pzSqlvarn++;
sCols--;
}
memcpy(pzAduexitp->XPUSRMSG ,'NAME COLUMN NOT FOUND' ,21);
return 8; /* Q U I T ! */
} else {
aduxitr1(1,pzAduexitp->XPUSERWA_); /* bump discard count */
memcpy(pzAduexitp->XPUSRMSG
+sizeof(pzAduexitp->XPUSRMSG)
-sizeof(szDiscard)
,szDiscard
,sizeof(szDiscard));
return 4; /* Q U I T ! */
}
}
pzSqlvarn++;
sCols--;
}
memcpy(pzAduexitp->XPUSRMSG ,'REGISTRATION COLUMN NOT FOUND' ,29);
return 8; /* Q U I T ! */
}
/*------------------------*
* Exit Initialization *
*------------------------*/
static int Initial(struct ADUEXITP *pzAduexitp){
short sCols, sMsgs;
char *pstrMsg;
struct LVAREA *pzLvarea;
struct SQLDA *pzSqlda;
struct SQLVARN *pzSqlvarn;
struct TBNAME {
char strCreator (|8|) ;
char strName (|18|) ;
} *pzTb;
if ( memcmp(pzAduexitp->XPUTIDA_ ,'JST' ,3) != 0){
return 4;
} else if (memcmp(((char*)pzAduexitp->XPTABLEA_)+8
,'STARSHIP' ,8) != 0){
return 4;
}
/* allocate user workarea */
if ( (pzLvarea=calloc(1,LVAREAZ))==NULL){
return GetmainError(1,pzAduexitp);
}
pzAduexitp->XPUSERWA_ = pzLvarea; /* remember for use later */
aduxitr1(0,pzLvarea);
memset(pzLvarea->LVKEEP ,' ' ,sizeof(LVBLANKZ));
pzTb = pzAduexitp->XPTABLEA_;
memcpy(pzLvarea->LVTBCREA ,pzTb->strCreator ,sizeof(pzLvarea->LVTBCREA));
memcpy(pzLvarea->LVTBNAME ,pzTb->strName ,sizeof(pzLvarea->LVTBNAME));
/* Say I am here, and have this table */
memcpy(pzAduexitp->XPUSRMSG ,szIgot2ini ,sizeof(szIgot2ini));
memcpy(pzAduexitp->XPUSRMSG+sizeof(szIgot2ini)
,pzAduexitp->XPTABLEA_
,sizeof(struct TBNAME));
pzSqlda = pzAduexitp->XPSQLDAA_;
sMsgs = pzSqlda->SQLN * sizeof(pzAduexitp->XPUSRMSG);
sMsgs += 4+256; /* adjust for prefix and pad */
pzAduexitp->xpusermz = (int)sMsgs;
if ( (pzAduexitp->XPUSERMA_=calloc(1,sMsgs))==NULL){
return GetmainError(2,pzAduexitp);
}
sCols = pzSqlda->SQLN;
pzSqlvarn = (struct SQLVARN*)( ((char*)pzSqlda)+SQLDSIZ);
pstrMsg = pzAduexitp->XPUSERMA_;
pstrMsg += 4;
sMsgs = 0;
while(sCols){
memset(pstrMsg ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
memcpy(pstrMsg ,pzSqlvarn->SQLNAM ,sizeof(pzSqlvarn->SQLNAM));
sMsgs++;
pstrMsg += sizeof(pzAduexitp->XPUSRMSG);
pzSqlvarn++;
sCols--;
}
/*
memcpy(pzAduexitp->XPUSERMA_ ,&sMsgs ,sizeof(sMsgs));
*/
*((short*)pzAduexitp->XPUSERMA_) = sMsgs;
*(((short*)pzAduexitp->XPUSERMA_)+1) = sizeof(pzAduexitp->XPUSRMSG);
return 0;
}
/*--------------------------------*
* Exit termination processing *
*--------------------------------*/
static int Cleanup(struct ADUEXITP *pzAduexitp){
short sMsgs;
char *pstrMsg;
struct LVAREA *pzLvarea;
pzLvarea = pzAduexitp->XPUSERWA_;
memcpy(pzAduexitp->XPUSRMSG ,szIgot2cln ,sizeof(szIgot2cln));
sMsgs = 0;
pstrMsg = pzAduexitp->XPUSERMA_;
pstrMsg += 4; /* bump past gunk */
aduxitr1(3,pzAduexitp->XPUSERWA_); /* unpack counts */
memset(pstrMsg ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
memcpy(pstrMsg ,pzLvarea->LVDISC ,sizeof(pzLvarea->LVDISC));
memcpy(pstrMsg+sizeof(pzLvarea->LVDISC)+1
,'DID NOT QUALIFY AS HEAVY CRUISERS'
,33);
pstrMsg += sizeof(pzAduexitp->XPUSRMSG);
sMsgs++;
memset(pstrMsg ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
memcpy(pstrMsg ,pzLvarea->LVKEEP ,sizeof(pzLvarea->LVKEEP));
memcpy(pstrMsg+sizeof(pzLvarea->LVKEEP)+1
,'QUALIFIED AS HEAVY CRUISERS'
,27);
pstrMsg += sizeof(pzAduexitp->XPUSRMSG);
sMsgs++;
memset(pstrMsg ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
memcpy(pstrMsg+sizeof(pzLvarea->LVKEEP)+1
,'** ALL HEAVY CRUISERS NOW UNLOADED **'
,37);
*((short*)pzAduexitp->XPUSERMA_) = sMsgs;
/* free message block if we acquired one */
if (pzAduexitp->XPUSERMA_ != NULL){
free(pzAduexitp->XPUSERMA_);
pzAduexitp->XPUSERMA_ = NULL;
}
/* free main workarea */
free(pzAduexitp->XPUSERWA_);
pzAduexitp->XPUSERWA_ = NULL;
return 0;
}
/*-------------------------------------*
* Error in acquiring local storage *
*-------------------------------------*/
static int GetmainError(int lType ,struct ADUEXITP *pzAduexitp){
memset(pzAduexitp->XPUSRMSG ,' ' ,sizeof(pzAduexitp->XPUSRMSG));
if (lType == 1){
memcpy(pzAduexitp->XPUSRMSG ,szMgetmerr ,sizeof(szMgetmerr));
} else {
memcpy(pzAduexitp->XPUSRMSG ,szMgetmer2 ,sizeof(szMgetmer2));
}
return 8;
}
/*
***************************
* D I S C L A I M E R *
***************************
This is a sample UNLOAD PLUS user exit written in SAS C.
This exit would be used in order to inspect and/or manipulate unload
data records prior to their being written to the sort or output
dataset.
This exit will only be invoked when it is specifically named
in the 'UNLOADEXIT ADUEXIC C' parameter.
Note: please review the documentation in the reference manual,
and the following usage notes prior to implementing this exit.
please call BMC Product Support with any questions you may
have in this area.
Phone: 1-800-841-2031 outside Texas
1-713-240-8800 in Texas
*******************************
* E X I T S C E N A R I O *
*******************************
As always it is tough to come up with a good example for an exit
that is intended to be application oriented. For the purposes of
this example, a DB2 table exists containing data on Federation
starships.
The DB2 table name is STARSHIP, containing the following columns:
REGISTRATION CHAR(16) NOT NULL,
NAME VARCHAR(64))
The purpose of this exit is to screen the starships by registration
number to filter out any non-heavy cruiser class starships.
In the star fleet, there were 12 heavy cruiser class starships
with registration numbers between NCC-1700 and NCC-1711.
This code only allows these records to be unloaded.
(This filtering could be done with unload control statements, but
since this data would appear on a report that could be stolen by
the Romulans, starfleet feels it is more secure to hide the logic
for selection in a program)
In addition, this screening is only done if the unload utility id
begins with the characters 'JST'.
The name must also be checked to ensure that it is not null since
this is a nullable field (clerical errors do occur occaisionally!)
the bottom line of all this is that this example shows how to:
1. Inspect the user exit block and pseudo SQLDA
2. Getmain storage needed that is preserved during the unload
3. Getmain storage for multiple user messages
4. Issue single and multiple messages for DB2 UNLOAD PLUS to issue
5. Find and look at unload table column names
6. Find and inspect data prior to being unloaded
7. Determine null fields
8. Print records being processed with messages
9. Accept and discard records from UNLOAD PLUS processing
*****************
* N O T E S *
*****************
ADUEXIC is called at 3 points in processing a table unload.
When invoked, r0 contains a function code.
r1 contains the address of a user exit block described by the
ADUEXITP dsect copied to your program from macro member @aduxprm.
fields for your use in this block are described in the dsect.
The main field of interest is the pseudo-sqlda pointer xpsqlda@
which points to an 'sqlda' prepared for each selected unload table.
this sqlda has the same format and contents of a db2 sqlda, with the
main exception being that the descriptions of the data fields are
the output descriptions (after conversions if any), and the data
pointers are pointing to the output record offsets in the record
about to be written (function call 0 only).
function:
Function code 0 = process record
this call is made after a record is prepared for writing.
all fields are converted ready for output. the sqlda
provides the field types and record positions of the data.
return codes from process:
0 = accept this record
4 = discard this record
anything else = terminate the run.
Function code 1 = initialization call
this call is made during table unload initialization. it
happens once per table to be unloaded to allow selection of
whether or not to process the table with the exit.
return codes from initialize:
0 = activate exit for records from this table
4 = don't use the exit for this table.
anything else = terminate the run.
Function code 2 = termination/cleanup call
this call is made just prior to termination of the unload,
to allow you to perform any cleanup functions necessary.
return codes for terminate are ignored.
for any exit function, the exit may insert message text in the
message area for printing upon return.
*/
Tip: For faster searching, add an asterisk to the end of your partial query. Example: cert*