API Programming in C

The following is a sample API program in the C language.

Note: There are slight differences between MVS and other platforms in the area of file open() and file close() as noted in the commented out steps in the body of this example. On all platforms, this example is supplied as edaapp.c; on MVS, this sample is supplied as qual.EDACTL.DATA(EDAAPC). The actual MVS version is delivered with the non-MVS steps commented out and the MVS steps are un-commented so the sample is ready to use.


Top of page

Example: C Source for EDAAPP

/*  EDAAPC C   --   A Sample API application in C */

#include "eda.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/* Sample application program. */
/* */
/* APP1 demonstrates client API behavior. */
/* APP1 gives the user a choice of servers, connects with one and then */
/* sends SQL commands to the server. */
/* */
/* The style used here is aimed at helping your comprehension. All of the */
/* features demonstrated here can be "lifted" to your application, IF it is */
/* appropriate for you. Before using any code examples from this program, */
/* be sure that you understand them! */
/* */
/* If you are reading this code as an example, start from the bottom (main) */
/* and then work your way through the calling tree. If you are executing */
/* this code in the debugger, simply step through each routine. */
/* */


/*-------------------------------------------------------------------------*/
/* The LoadEdaErrors routine is called to construct a table to relate */
/* error codes to message text. It does this by using */
/* the EDABROWSE feature to create a table of error messages */
/* which it then reads it. In it is always permitted */
/* to connect to "self" to obtain this table. */
/* The message table fields are created at this start of */
/* the application and are then referenced during the */
/* program's operation. */
/*-------------------------------------------------------------------------*/
typedef struct
{
long code;
char szText[128];
}MSGLIST;

typedef EDAPTR(MSGLIST) PMSGLIST;


static PMSGLIST msghead = NULL;
static int msgCount = 0;

static int LoadEdaErrors(EDA_ID * peid)
{
EDA_SCB scbself;
PMSGLIST pmsg;
long nts=EDA_NULL_DELIM;
long toself=EDA_CONNECT_SELF;
long field=0;
long ftype;
long errtbl=EDABASE_TBL_ERRORS;

/* connect to 'self' */
EDACONNECT(peid, &scbself, "",&nts,"",&nts,"",&toself);
if (!msghead)
{
field = 0;
/* tell EDABROWSE to return error table*/
EDABROWSE(&scbself,&errtbl,NULL,NULL,NULL);

/* wait until answer set ready */
EDATEST(&scbself,NULL);

/* create table based on returned size of error table */
msgCount = (int)scbself.count;
msghead = calloc((int)msgCount, sizeof(MSGLIST));
pmsg = msghead;
for (;;)
{
long ll;
char szString[128];
long size;
EDANEXT(&scbself);
if (scbself.status == EDA_END_OF_SET)
{
break;
}
else if (scbself.status != EDA_SUCCESS)
{
printf("failed loading error codes (%ld)\n", scbself.status);
return 1;
}
memset(szString,0,sizeof(szString));

/* get the error code data from field 1 in BINARY format */
field=1L;
ftype = EDA_BINARY;
size=sizeof(long);
EDAFIELD(&scbself,&field,(EDACPTR)&ll,&size,&ftype);

/* get the text data from field 2 in ALPHA format */
field=2L;
ftype = EDA_ALPHANUM;
size=sizeof(szString);
EDAFIELD(&scbself,&field,szString,&size,&ftype);

/* load a structure */
pmsg->code = ll;
strcpy(pmsg->szText,szString);
pmsg++;
}

}
EDAXCONNECT(&scbself); /* disconnect from 'self' */
return 0;
}

/*----------------------------------------------------------------------------*/
/* Fill in a caller's buffer with the text associated */
/* with the message */
/*----------------------------------------------------------------------------*/
void GetMessageText(long status, char * pszBuffer)
{

PMSGLIST pmsg;
int i;
pmsg = msghead;

for (i=0;i<msgCount;i++,pmsg++)
{
if (pmsg->code == status)
{
sprintf(pszBuffer,"Ret Code=%ld: %.38s", status,pmsg->szText);
break;
}
}
if (i == msgCount)
{
sprintf(pszBuffer,"Untabled API/SQL error %ld", status);
}
}

/*----------------------------------------------------------------------------*/
/* check_stat() */
/* */
/* Print status message based on current error status code */
/* */
/*----------------------------------------------------------------------------*/

static long check_stat(EDA_scb *pscb, char *cmd, long status)
{
char m_str[256];

if (status == EDA_END_OF_SET && pscb->count == -1)
{
printf("\n<<< No data returned. >>>\n");
}
else if (status == EDA_END_OF_SET)
{
printf("\n\n<<< %ld record(s) processed. >>>\n", pscb->count);
}
else if (status != 0)
{
strncpy(m_str, cmd, 12);
m_str[12] = '\0';
printf("\n[APP1] %s : ", m_str);
GetMessageText(status,m_str);
printf("%s\n", m_str);
}
return(status);
}

FILE *trceh = NULL; /* file pointer for trace file */

/* this callback, when established, captures traces to a file */

/*----------------------------------------------------------*/
/* right trim a string */
/*----------------------------------------------------------*/
static void trim(char * szTxt)
{
char * p;
p=szTxt+strlen(szTxt)-1;
while (*p == ' ')
{
*p=0;
p--;
}

}

static EDA_APIENTRY callback(PEDA_CBB pcbb)
{
if (!trceh) /* 34398 add disk file for rdaapp tracing */
{ /* [36272] lowercase names as part of cleanup project */
trceh = fopen("edaapp.trc", "w"); /* Use this for non MVS */
/* trceh = fopen("DD:APPTRC" , "w"); */ /* Use this for MVS */
}
if (trceh)
{
trim(pcbb->u.trace.msg);
if (pcbb->subcode)
fprintf(trceh, "EDA %d> (%5.2f) (%ld) %s\n",
(int) pcbb->u.trace.event,
(float) pcbb->subcode / 100,
pcbb->sid, pcbb->u.trace.msg);
else
fprintf(trceh, "EDA %d> (%ld) %s\n",
(int) pcbb->u.trace.event,
pcbb->sid, pcbb->u.trace.msg);
}

}

static long one = 1L;

/* this macro sets up EDAPINIT array based upon known names */
#define PINITSET(thing, val) pTypeArray[setcount] = thing; \
pValArray[setcount] = val; \
setcount++

static const long EdaTraceVals[]={
EDA_VAR_TRACE_LVL1,EDA_VAR_TRACE_LVL2,EDA_VAR_TRACE_LVL3,
EDA_VAR_TRACE_LVL4,EDA_VAR_TRACE_LVL5,EDA_VAR_TRACE_LVL6,
EDA_VAR_TRACE_LVL7,EDA_VAR_TRACE_LVL8};

static const long OdinTraceVals[]=
{ EDA_VAR_ODIN_LVL1,EDA_VAR_ODIN_LVL2,EDA_VAR_ODIN_LVL3,
EDA_VAR_ODIN_LVL4,EDA_VAR_ODIN_LVL5};

long setcount; /* macro increments this */

/* these arrays are used by EDAPINIT for presetting certain variables */
/* this first array selects the EDASET variable (defined in eda.h) */
long pTypeArray[EDA_MAX_PINIT] = { 0 };
/* the following two arrays are mutually exclusive */
/* this array points to any callbacks to be set : */
EDA_ECALLBACK pRtnArray[EDA_MAX_PINIT] = {NULL};
/* this array points to any values to be set : */
void *pValArray[EDA_MAX_PINIT] = { NULL};

void setupTraces(char *tracelevel)
{
int i;
int tlevel;

PINITSET(EDA_CB_TRACE,NULL);
pRtnArray[setcount]= (EDA_ECALLBACK)callback;
setcount++;
PINITSET(EDA_VAR_TRACE,&one);
for (i = 0; tracelevel[i] != ' ' && tracelevel[i] != '\0'
&& tracelevel[i] != '/' && i < 8; i++)
{
tlevel = tracelevel[i] - '0';
switch (tlevel)
{
case 1:case 2:case 3:case 4:
case 5:case 6:case 7:case 8:
PINITSET(EdaTraceVals[tlevel-1],&one);
break;
default:
printf("ERROR : Unknown trace level %c\n",
tracelevel[i]);
break;
}

}
if (tracelevel[i] == '/') /* ODIN levels requested */
{
int j;
for (i++, j = i+5; tracelevel[i] != ' '
&& tracelevel[i] != '\0' && i < j; i++)
{
tlevel = tracelevel[i] - '0';
switch (tlevel)
{
case 1:case 2:case 3:case 4:case 5:
PINITSET(OdinTraceVals[tlevel-1],&one);
break;
default:
printf("ERROR : Unknown Comms (ODIN) level %c\n",
tracelevel[i]);
break;
}
}
}
return;

}

/*----------------------------------------------------------------------------*/
/* initialize_eda() */
/* */
/* Initialize the API */
/* */
/*----------------------------------------------------------------------------*/

static void initialize_eda(EDA_ID *peid, long *status)
{
long MAJOR_VERSION = EDA_VAR_MAJOR;
long MINOR_VERSION = EDA_VAR_MINOR;
long VAR_AUTOCOMMIT = EDA_VAR_AUTOCOMMIT;
long VAR_OFF = 0;
long major;
long minor;
char tracelvl[40] = {0};

/* tracing should be set on at INIT time */

printf("\nEnter desired trace levels, <CR> for none: ");
fflush(stdout); /* Some block mode environments need a fflush for gets() */
gets(tracelvl);

EDAINSPECT(NULL, NULL, &MAJOR_VERSION, &major, status);
EDAINSPECT(NULL, NULL, &MINOR_VERSION, &minor, status);
printf("\nAPP 1 : Initializing API, Version %ld, Release %ld\n",
major, minor);

/* EDAINIT is always the first function called */

if (!tracelvl[0])
EDAINIT(peid, status);
else
{
setupTraces(tracelvl);

/* EDAPINIT is a substitute call to EDAINIT that sets */
/* variables (such as eda traces) at start-up time */
EDAPINIT(peid, &setcount, (long *)pTypeArray,
pRtnArray, pValArray, status);
}

if (check_stat(NULL, "EDAINIT ", *status) < EDA_SUCCESS)
return;

/* with a valid EDA_ID, you now can connect to self
and fill the error message table */

LoadEdaErrors(peid);

/* Turn off AUTO COMMIT */

EDASET(peid, NULL, &VAR_AUTOCOMMIT, &VAR_OFF, status);
}

/*----------------------------------------------------------------------------*/
/* choose_server() */
/* */
/* Display the available entities (servers) and allow the user to */
/* choose one for this session, then connect to it. */
/* */
/* No error checking of any sort is done here. This includes string gets */
/* buffer overflow checks, etc. A valid application would, of course, */
/* check for this kind of error. */
/*----------------------------------------------------------------------------*/

static void choose_server(EDA_ID *peid, EDA_scb *pscb, long * pstatus)
{
long n_servers = EDA_VAR_SERVERCOUNT;
long maxbuf;

char user[16] = {0};
char server[16] = {0};
char password[16] = {0};

long str_len = EDA_NULL_DELIM;
char buffr[256];
long j;
long cServers; /* will get number of servers from EDAINSPECT */

/* Get the number of servers (entities) returned in ii */



EDAINSPECT(peid, NULL, &n_servers, &cServers, pstatus);
if (cServers == 0)
{
printf("\nNo servers are available\n");;
exit (4);
}

/* The server names have a maximum of 8 characters and are blank padded
to 8 if the name is less than 8. */

maxbuf = sizeof(buffr);

EDASERVERS(peid, buffr, &maxbuf, pstatus);

buffr[cServers * 8] = '\0';
printf("\nThe following %ld servers are available:", cServers);
for (j=0; j < cServers; j++)
{
/* The server names are placed end to end in the buffer, NOT NULL terminated,
therefore the format %.8s is necessary */

printf(" %.8s", &buffr[j*8]);
}
printf("\n");

/* Show the first server as a default to the user */

buffr[8] = '\0'; /* NULL terminate the 1st entity as a string */
/* The rest of buffr[] is scratch space */

printf("\nEnter Server name (Hit return for '%s') : ", buffr);
fflush(stdout); /* Some block mode environments need a fflush for gets() */
gets(server);

if (server[0] == '\0')
{
strcpy(server,buffr); /* take this first as the default as promised */
}

printf("Enter user ID: ");
fflush(stdout); /* Some block mode environments need a fflush for gets() */
gets(user);

printf("Enter password: ");
fflush(stdout); /* Some block mode environments need a fflush for gets() */
gets(password);

/* Establish the synchronous connection with the server. */

printf("Connecting....");
EDACONNECT(peid, pscb, user, &str_len, password, &str_len, server, &str_len);
*pstatus = pscb->status;
}

/*----------------------------------------------------------------------------*/
/* check_msg() */
/* */
/* Print all pending messages on standard output */
/* */
/*----------------------------------------------------------------------------*/

void check_msg(EDA_scb *pscb)
{
char m_str[40];

if (pscb->msg_type == 1)
{
strcpy(m_str, "\n[EDA-");
}
else
{
strcpy(m_str, "\n[RPC-");
}

strncat(m_str, pscb->msg_org, 8);
m_str[13] = '\0';
if (memcmp(pscb->msg_org, "SMART ", 8) || pscb->msg_text[0] != '0')
{
printf("%s] (%5ld) ", m_str, pscb->msg_code);
printf("%*s\n", pscb->msg_len, pscb->msg_text);
}

while (pscb->msg_pending)
{
EDAACCEPT(pscb);
if (pscb->msg_type == 1)
{
strcpy(m_str, "\n[EDA-");
}
else if (pscb->msg_type == 2)
{
strcpy(m_str, "\n[RPC-");
}
else /* if not 1 or 2, message is from internal API */
{
strcpy(m_str, "\n[API-");
}
strncat(m_str, pscb->msg_org, 8);
m_str[13] = '\0';
if (memcmp(pscb->msg_org, "SMART ", 8) || pscb->msg_text[0] != '0')
{
printf("%s] (%5ld) ", m_str, pscb->msg_code);
printf("%*s\n", pscb->msg_len, pscb->msg_text);
}
}
}

/* a binary dump */
static void bin_display(char *loc, long length)
{
static char *hexchars = "0123456789ABCDEF";
int val;
int i;

for (i = 0; i < (int) length; i++)
{
val = (loc[i] & 0xF0) >> 4;
printf("%c", hexchars[val]);
val = (loc[i] & 0x0F);
printf("%c", hexchars[val]);
}

printf(" ");
}

static void null_display(long length)
{
while (length-- > 0)
{
printf("+");
}

printf(" ");
}

/*----------------------------------------------------------------------------*/
/* int process_response() */
/* */
/* Receive an answer set from the server after an SQL or RPC call. */
/* Zero, one or more messages may be received with the answer set. */
/* */
/* The last status is returned (success, error, etc). */
/* */
/*----------------------------------------------------------------------------*/

static int process_response(EDA_scb *pscb)
{
EDA_info_area info_area;
char *tup; /* Pointer to tuple buffer */
char *fld; /* Pointer to field within buffer */
long cnbr; /* Column number */
long fmt_type = EDA_ALPHANUM;
long result;
long lColInfo = EDA_VAR_COLINFO_PTR;
EDAPTR(EDA_COLINFO) pColInfo;

/* wait for response on this synchronous scb */
EDATEST(pscb, NULL);
if (pscb->status != EDA_NO_FIELD_INFO &&
check_stat(pscb, pscb->command, pscb->status) < EDA_SUCCESS)
{
return(pscb->status);
}

/* Determine the size of response information and allocate a buffer */
tup = (char *) calloc(1, (int) pscb->a_size + 1);
if (tup == NULL)
{
return(check_stat(pscb, pscb->command, EDA_MEMORY_OVERFLOW));
}

/* Establishing a colinfo array allows this application to review NULLity repidly.
The array if filled in by the EDA API at each EDAFETCH.
*/
pColInfo = calloc((pscb->nbrcols)+1, sizeof(EDA_COLINFO));
if (pColInfo == NULL)
{
return(check_stat(pscb, pscb->command, EDA_MEMORY_OVERFLOW));
}

EDASET(NULL, pscb, &lColInfo, pColInfo, &result);
/* Process tuples from the answer set and display messages until an end */
/* of set status is returned by EDAFETCH. */

for ( ;; )
{
EDAFETCH(pscb, tup, &pscb->a_size, &fmt_type);
if (pscb->status == EDA_END_OF_SET)
{
printf("\nEnd of set\n\n");
break;
}
if (check_stat(pscb, pscb->command, pscb->status) < EDA_SUCCESS)
{
return(pscb->status);
}

if (pscb->msg_type != 0)
{
check_msg(pscb);
}
printf("\n ");
cnbr = 0;
while (++cnbr <= pscb->nbrcols)
{
EDAINFO(pscb, &cnbr, &info_area);
if (check_stat(pscb, pscb->command, pscb->status) < EDA_SUCCESS)
{
return(pscb->status);
}
/* check the colinfo array indexed by the column number to check for NULL */
if ((pColInfo+cnbr)->colInfo == EDA_COLINFO_NULL)
{
null_display(info_area.length);
continue;
}
fld = tup + info_area.a_offset;
switch ((short) info_area.type)
{
case EDA_I_TYPE_CODE:
case EDA_F_TYPE_CODE:
case EDA_D_TYPE_CODE:
case EDA_P_TYPE_CODE:
printf("%*.*s ", info_area.length, info_area.length, fld);
break;
case EDA_A_TYPE_CODE:
case EDA_K_TYPE_CODE:
printf("'%*.*s' ", info_area.length, info_area.length, fld);
break;
case EDA_DATE_TYPE_CODE:
printf("%4.4s-%2.2s-%2.2s ", fld, fld+4, fld+6);
break;
case EDA_B_TYPE_CODE:
bin_display(fld, info_area.b_size);
break;
}
}
}

free(tup);
free(pColInfo);

return(pscb->status);
}


/*----------------------------------------------------------------------------*/
/* query_results() */
/* */
/* Walk through the answer set(s) of a query and write to standard out. */
/*----------------------------------------------------------------------------*/
static void query_results(EDA_ID *peid, EDA_scb *pscb, long *status)
{
int more = 1;
int answer_sets = 0;

if (pscb->msg_type != 0)
check_msg(pscb);

while (more)
{
*status = process_response(pscb);
if (*status && (*status != EDA_END_OF_SET) )
{
return;
}

EDAQUIT(pscb);

if (pscb->status == EDA_END_OF_TRAN)
{
more = 0;
}

if (pscb->msg_type != 0)
{
check_msg(pscb);
}
answer_sets++;
}

if (answer_sets > 1)
{
printf("\nServer responded with %d answer sets.\n", answer_sets);
}
}

/*----------------------------------------------------------------------------*/
/* process_queries() */
/* */
/* Accept a query from standard input. Queries are terminated by semi- */
/* colons (';'). Each query is limited to 800 bytes. An empty query will */
/* signal completion of input (you can hit EOF) */
/* */
/* Query results, both data and messages are handled by query_results() */
/*----------------------------------------------------------------------------*/

static void process_queries(EDA_ID *peid, EDA_scb *pscb, long *status)
{
long est1 = 1L;
long est2 = 1L;
long ZERO = 0L;
long ONE = 1L;
int SLEN = 799;
char sbuffer[800];
int c;
long i;
for (;;)
{
printf("Enter SQL queries terminated ");
printf("with a ';' Enter 'quit' to end.\n\n");

do
{
do
{
c = getchar();
} while (isspace(c));
for (i=0; i < SLEN && c != EOF && c != ';'; i++)
{
if (isspace(c)) /* Turn <cr>'s & tabs into ordinary spaces */
c = ' ';
else
if (c == '\\') /* Use a \ as an escape character */
c = getchar();
sbuffer[i] = (char)c;
if (memcmp(sbuffer, "quit", 4) == 0 ||
memcmp(sbuffer, "QUIT", 4) == 0)
{
return;
}
c = getchar();
}
if (i <= 0)
{
return;
}
sbuffer[i++] = ';';


printf("Sending request, waiting for response....\n");
EDASQL(pscb, sbuffer, &i, &est1, &est2, NULL, &ZERO, &ONE);
EDATEST(pscb, NULL); /* synchronous wait */
*status = pscb->status;
if (pscb->status != EDA_NO_FIELD_INFO &&
check_stat(pscb, "EDATEST ", pscb->status) < EDA_SUCCESS)
{
return;
}

query_results(peid, pscb, status);
if (*status > EDA_SUCCESS &&
*status != EDA_END_OF_SET &&
*status != EDA_END_OF_TRAN )
{ /* Ignore warning states */
*status = EDA_SUCCESS;
}
} while (*status == EDA_SUCCESS && c != EOF);
}
}

/*----------------------------------------------------------------------------*/
/* main() */
/* */
/* Initialize API, Connect to the server entity chosen by the */
/* user, process SQL queries from the user and then terminate the session */
/* with the server. */
/* */
/*----------------------------------------------------------------------------*/

int main(int argc, char *argv[])
{
EDA_ID eid; /* EDA id for this application. */
EDA_scb scb; /* An scb. */
long status; /* Status return codes from API */

initialize_eda(&eid, &status);
if (check_stat(&scb, scb.command, status) != EDA_SUCCESS)
exit(1);

choose_server(&eid, &scb, &status);
if (check_stat(&scb, scb.command, status) != EDA_SUCCESS)
{
printf("Unsuccessful\n");
}
else
{
printf("Successful\n");
process_queries(&eid, &scb, &status);
}
EDATERM(&eid, &status);

if (trceh) /* use this for non-MVS */
fclose(trceh); /* use this for non-MVS */

if (msghead)
free(msghead);
return 0;
}

iWay Software