/*---------------------------------------------------------------------+
| Copyright (c) 1995, SAS Institute Inc. |
| Unpublished - All Rights Reserved |
| S A S / C S A M P L E |
| |
| |
| NAME: SASCALL |
| LANGUAGE: C (CICS) |
| PURPOSE: Traditional CICS FILEA sample program used in |
| Appendix 1, Second Edition, of the SAS/C CICS User's |
| Guide. See this appendix for a more detailed |
| explanation of the SASCALL, SASCMNU, SASCBRW samples |
| as well as the SASCAMA, SASCAMB, and SASCAMC BMS |
| maps. |
| MVS - |
| PREPROCESS, COMPILE, LINK: Submit prefix.SAMPLE.AUX(SASCALL) |
| where "prefix" is the installation defined |
| high-level qualifier for the SAS/C product. |
| EXECUTE: See Appendix 1, Second Edition, of the SAS/C CICS |
| User's Guide for additional information. Use the |
| sample PPT input (prefix.SAMPLE.AUX(SASCTBLS) to |
| define the resources to CICS. |
| TSO - |
| PREPROCESS: LCCCP CLIST |
| COMPILE: LC370 CLIST using the RENT option. |
| LINK: CLK370B CLIST using the CICS and RENT options. |
| EXECUTE: see EXECUTE under MVS above |
| CMS - |
| PREPROCESS: LCCCP EXEC |
| COMPILE: LC370 EXEC using the RENT option. |
| CLINK: CLINK EXEC using the CICS option; resulting object code|
| must be ported to an MVS operating system for final |
| link-editing with the CICS Execution Interface stub |
| routines. |
| LINK: Use the LCCCXXL cataloged procedure using the NOCLINK |
| and RENT options. Input must be specified via the |
| SYSLIN DD statement, not the SYSIN DD statement. |
| EXECUTE: see EXECUTE under MVS above |
| |
+---------------------------------------------------------------------*/
#include <dfhbmsca.h> /* Useful BMS definitions */
#include <sascaga.h> /* C struct defining Map A, produced by DSECT2C */
#include <sascagb.h> /* C struct defining Map B, produced by DSECT2C */
#include <string.h> /* standard string header file */
/* General purpose variables */
char messages[39]; /* Message holding area */
/* The following two strings are used to validate input data */
char *valid_name_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ .-'";
char *valid_amount_chars = "0123456789.$[";
short comlen; /* Length of the CICS commarea */
char keynum[6]; /* Record key, the account number */
int i;
/* The FILEA record layout */
struct FILEA
{
char stat;
char numb[6];
char name[20];
char addrx[20];
char phone[8];
char datex[8];
char amount[8];
char comment[9];
} filea;
/* Definition of the commarea */
struct FILEA commarea;
/* Function prototypes */
void data_error(void);
void map_build(void);
void map_send(void);
void cics_control(void);
void notmodf(void);
void duprec(void);
void badleng(void);
void badchars(void);
void notfound(void);
void mfail(void);
void errors(void);
void smnu(void);
/* The main function begins here */
void main(struct EIBLK *eib_pointer, void *commarea_pointer)
/* Note that "_commptr", "_eibptr", and "_dibptr" are */
/* implementation provided global external variables */
/* available to all SAS/C CICS programs. You may, as */
/* demonstrated in this program, also define your own */
/* pointers to the these commonly used areas. */
{
if (commarea_pointer) goto read_input; /* Is this the 2nd time thru? */
/* If 1st time thru, present the initial screen */
EXEC CICS HANDLE CONDITION ERROR(errors) MAPFAIL(mfail);
EXEC CICS RECEIVE MAP("SASCAGA");
if (memcmp(sascaga.keyl,"\0\0",2) == 0) /* A key must be provided */
{
badleng(); /* No return from badleng() */
}
for(i=0;i<=5;i++) /* Verify that all 6 digits of the key are numeric */
{
if (isdigit((int) sascaga.KEYI[i]) == 0)
{
strcpy(messages,"ACCOUNT NUMBER MUST BE NUMERIC");
smnu(); /* There is no return from smnu */
}
}
memcpy(keynum,sascaga.KEYI,sizeof(sascaga.KEYI)); /* Save the key */
memset(&sascagb,'\0',SASCAGBE); /* Clear the map */
if (memcmp(eib_pointer->EIBTRNID,"SADD",4) == 0) /* Is this an add? */
{
/* Set up Map B for an add operation */
strcpy(sascagb.titleo,"FILE ADD");
strcpy(sascagb.msg3o,"ENTER DATA AND PRESS ENTER KEY");
memcpy(&sascagb.numbo,sascaga.KEYI,sizeof(sascaga.KEYI));
memcpy(&commarea.numb,sascaga.KEYI,sizeof(sascaga.KEYI));
sascagb.amounta = DFHBMUNN;
comlen = 7; /* The commarea contains only status and account no. */
map_send(); /* Cause Map B to be displayed */
cics_control(); /* Cause control to pass back to CICS */
}
else
if (memcmp(eib_pointer->EIBTRNID,"SINQ",4) == 0) /* Is this a query?*/
{
/* Try to read the record specified, */
/* but be prepared if it is not */
/* found! */
EXEC CICS HANDLE CONDITION NOTFND(notfound);
EXEC CICS READ DATASET("FILEA") INTO(&filea)
RIDFLD(keynum) LENGTH(sizeof(struct FILEA))
KEYLENGTH(sizeof(keynum));
/* The record was found, so set up Map B for a query */
strcpy(sascagb.titleo,"FILE INQUIRY");
strcpy(sascagb.msg3o,"PRESS ENTER TO CONTINUE");
map_build(); /* Cause the data to be moved from the record */
/* area into the map fields. */
sascagb.namea = DFHBMPRO; /* Protect all the fields */
sascagb.addra = DFHBMPRO;
sascagb.phonea = DFHBMPRO;
sascagb.datea = DFHBMPRO;
sascagb.amounta = DFHBMPRO;
sascagb.commenta = DFHBMPRO;
map_send(); /* Cause Map B to be displayed */
EXEC CICS RETURN TRANSID("SMNU"); /* Return to CICS, and cause */
/* the main menu to be dis- */
/* played again. */
}
else
if (memcmp(eib_pointer->EIBTRNID,"SUPD",4) == 0) /*Is it an update?*/
{
/* Try to read the record specified, */
/* but be prepared if it is not */
/* found! */
EXEC CICS HANDLE CONDITION NOTFND(notfound);
EXEC CICS READ DATASET("FILEA") INTO(&filea)
RIDFLD(keynum) LENGTH((short) sizeof(struct FILEA))
KEYLENGTH((short) sizeof(keynum));
/* The record was found, so set up Map B for an update */
strcpy(sascagb.titleo,"FILE UPDATE");
strcpy(sascagb.msg3o,"CHANGE FIELDS AND PRESS ENTER");
memcpy(&commarea,&filea,sizeof(struct FILEA)); /* Save the record */
/* in the commarea */
/* for later com- */
/* parison! */
map_build(); /* Cause the data to be moved from the record */
/* area into the map fields. */
map_send(); /* Cause Map B to be displayed */
comlen = 80; /* The commarea contains the entire record */
cics_control(); /* Cause control to pass back to CICS */
}
else
{
errors(); /* The transaction ID was not recognized */
/* There is no return from errors() */
}
read_input: /* The 2nd time thru, this is where */
/* things begin to happen */
memcpy(&commarea,commarea_pointer,sizeof(commarea)); /* Retrieve the */
/* commarea */
/* Prepare to handle error conditions */
EXEC CICS HANDLE CONDITION MAPFAIL(notmodf) DUPREC(duprec)
ERROR(errors) NOTFND(notfound);
/* Read the input map */
EXEC CICS RECEIVE MAP("SASCAGB");
if (memcmp(eib_pointer->EIBTRNID,"SUPD",4) == 0) /*Is this an update?*/
{
/* Try to read the specified record with intent to update */
EXEC CICS READ UPDATE DATASET("FILEA") INTO(&filea)
RIDFLD(commarea.numb)
LENGTH((short) sizeof(struct FILEA))
KEYLENGTH((short) sizeof(commarea.numb));
/* If the record just read does not exactly match the one read */
/* during the 1st pass, another user must have updated the */
/* the record while this transaction was rolled out. If so, */
/* refuse to update the record! */
if (memcmp(&filea,&commarea,sizeof(struct FILEA)))
{
/* Refuse to update, prepare screen for another attempt */
strcpy(sascagb.msg1o,"RECORD UPDATED BY OTHER USER, TRY AGAIN");
sascagb.msg1a = DFHBMASB;
sascagb.msg3a = DFHPROTN;
map_build(); /* Cause the data to be moved from the record */
/* area into the map fields. */
/* Resend only the data portion of the map */
EXEC CICS SEND MAP("SASCAGB") DATAONLY;
commarea = filea; /* Make sure that the same record is used */
comlen = 80; /* during the next pass */
cics_control(); /* Cause control to pass back to CICS */
}
else
{
/* It is OK to update the record. Say so on the next screen */
filea.stat = 'U';
strcpy(messages,"RECORD UPDATED");
}
}
else
if (memcmp(eib_pointer->EIBTRNID,"SADD",4) == 0) /* Is this an add?*/
{
/* Yes, it's an add. Say so on the next screen */
filea.stat = 'A';
strcpy(messages,"RECORD ADDED");
}
else
{
errors(); /* The transaction ID was not recognized */
/* There is no return from errors() */
}
/* Check to see if any of the fields were modified */
if (memcmp(sascagb.namel,"\0\0",2) == 0 &&
memcmp(sascagb.addrl,"\0\0",2) == 0 &&
memcmp(sascagb.phonel,"\0\0",2) == 0 &&
memcmp(sascagb.datel,"\0\0",2) == 0 &&
memcmp(sascagb.amountl,"\0\0",2) == 0 &&
memcmp(sascagb.commentl,"\0\0",2) == 0)
{
/* No fields were modified, so take no action */
notmodf(); /* There is no return from notmodf() */
}
if (memcmp(eib_pointer->EIBTRNID,"SADD",4) == 0) /* Is this an add? */
{
/* It's an add, so validate the name field */
for(i=0;i < *(short *) sascagb.namel ;i++)
{
if (strchr(valid_name_chars,sascagb.NAMEI[i]) == 0)
{
data_error(); /* There is no return from data_error */
}
}
/* Enforce the account number to be what was originally stated */
memcpy(&filea.numb,&commarea.numb,sizeof(commarea.numb));
}
else
if (memcmp(eib_pointer->EIBTRNID,"SUPD",4) == 0) /*Is this an update?*/
{
/* It's an update, so validate the name field if it was changed */
if (memcmp(sascagb.namel,"\0\0",2) != 0)
{
for(i=0;i < *(short *) sascagb.namel ;i++)
{
if (strchr(valid_name_chars,sascagb.NAMEI[i]) == 0)
{
data_error(); /* There is no return from data_error */
}
}
}
/* Also, validate the amount field, if it was changed */
if (memcmp(sascagb.amountl,"\0\0",2) != 0)
{
for(i=0;i < *(short *) sascagb.amountl ;i++)
{
if (strchr(valid_amount_chars,sascagb.AMOUNTI[i]) == 0)
{
data_error(); /* There is no return from data_error */
}
}
}
}
/* Update the record area with any fields that were changed on screen */
if (memcmp(sascagb.namel,"\0\0",2) != 0)
{
memcpy(&filea.name,sascagb.nameo,sizeof(sascagb.nameo));
}
if (memcmp(sascagb.addrl,"\0\0",2) !=0)
{
memcpy(&filea.addrx,sascagb.addro,sizeof(sascagb.addro));
}
if (memcmp(sascagb.phonel,"\0\0",2) !=0)
{
memcpy(&filea.phone,sascagb.phoneo,sizeof(sascagb.phoneo));
}
if (memcmp(sascagb.datel,"\0\0",2) !=0)
{
memcpy(&filea.datex,sascagb.dateo,sizeof(sascagb.dateo));
}
if (memcmp(sascagb.amountl,"\0\0",2) !=0)
{
memcpy(&filea.amount,sascagb.amounto,sizeof(sascagb.amounto));
}
else
{
/* For an add, fill in the default amount */
if (memcmp(eib_pointer->EIBTRNID,"SADD",4) == 0)
{
strcpy(filea.amount,"$0000.00");
}
}
if (memcmp(sascagb.commentl,"\0\0",2) !=0)
{
memcpy(&filea.comment,sascagb.commento,sizeof(sascagb.commento));
}
/* If this is an update, rewrite the record to the file */
/* If this is an add, write the record to the file for the 1st time */
if (memcmp(eib_pointer->EIBTRNID,"SUPD",4) == 0)
{
EXEC CICS REWRITE DATASET("FILEA") FROM(&filea)
LENGTH((short) sizeof(filea));
}
else
{
EXEC CICS WRITE DATASET("FILEA") FROM(&filea)
RIDFLD(commarea.numb) LENGTH((short) sizeof(filea));
}
smnu(); /* There is no return from smnu() */
} /* end of main() */
/* The data_error function is called when invalid data is entered */
/* in one of the Map B fields. The data error message is added */
/* and the screen is refreshed for another attmempt. */
void data_error()
{
sascagb.msg3a = DFHBMASB;
strcpy(sascagb.msg3o,"DATA ERROR - CORRECT AND PRESS ENTER");
sascagb.amounta = DFHUNNUM;
sascagb.namea = DFHBMFSE;
sascagb.addra = DFHBMFSE;
sascagb.phonea = DFHBMFSE;
sascagb.datea = DFHBMFSE;
sascagb.commenta = DFHBMFSE;
EXEC CICS SEND MAP("SASCAGB") DATAONLY;
/* Note that in the following statement, the implementation defined */
/* global external variable "_eibptr" is used instead of the appl- */
/* cation defined local variable "eib_pointer" from main(). */
if (memcmp(_eibptr->EIBTRNID,"SADD",4) == 0)
{
comlen = 7; /* If it's an add, only the account number is passed */
}
else
{
comlen = 80; /* Otherwise, the entire record is passed */
}
cics_control(); /* Cause control to pass back to CICS */
} /* end of data_error() */
/* This function moves all the fields from the record area into */
/* the map areas. */
void map_build(void)
{
memcpy(sascagb.numbo,filea.numb,sizeof(filea.numb));
memcpy(sascagb.nameo,filea.name,sizeof(filea.name));
memcpy(sascagb.addro,filea.addrx,sizeof(filea.addrx));
memcpy(sascagb.phoneo,filea.phone,sizeof(filea.phone));
memcpy(sascagb.dateo,filea.datex,sizeof(filea.datex));
memcpy(sascagb.amounto,filea.amount,sizeof(filea.amount));
memcpy(sascagb.commento,filea.comment,sizeof(filea.comment));
return;
} /* end of map_build() */
/* This function displays Map B upon the screen */
void map_send(void)
{
EXEC CICS SEND MAP("SASCAGB") ERASE;
return;
} /* end of map_send() */
/* This function returns control to CICS. It causes the same */
/* transaction to be re-initiated with a commarea. This is */
/* commonly known as a Psuedo-conversational "rollout". */
void cics_control(void)
{
/* Note that in the following statement, the implementation defined */
/* global external variable "_eibptr" is used instead of the appli- */
/* cation defined local variable "eib_pointer" from main(). */
EXEC CICS RETURN TRANSID(_eibptr->EIBTRNID) COMMAREA(&commarea)
LENGTH(comlen);
} /* end of cics_control() */
/* This function prepares an error message and calls smnu */
void notmodf(void)
{
strcpy(messages,"RECORD NOT MODIFIED");
smnu(); /* There is no return from smnu() */
} /* end of notmodf() */
/* This function prepares an error message and calls smnu */
void duprec(void)
{
strcpy(messages,"DUPLICATE RECORD");
smnu(); /* There is no return from smnu() */
} /* end of duprec */
/* This function prepares an error message and calls smnu */
void badleng(void)
{
strcpy(messages,"PLEASE ENTER AN ACCOUNT NUMBER");
smnu(); /* There is no return from smnu() */
} /* end of badleng() */
/* This function prepares an error message and calls smnu */
void badchars(void)
{
strcpy(messages,"ACCOUNT NUMBER MUST BE NUMERIC");
smnu(); /* There is no return from smnu() */
} /* end of badchars() */
/* This function prepares an error message and calls smnu */
void notfound(void)
{
strcpy(messages,"INVALID NUMBER - PLEASE REENTER");
smnu(); /* There is no return from smnu() */
} /* end of notfound() */
/* This function prepares an informational message and calls smnu */
void mfail(void)
{
strcpy(messages,"PRESS CLEAR TO EXIT");
smnu(); /* There is no return from smnu() */
} /* end of mfail() */
/* This function prepares an error message and calls smnu */
void errors(void)
{
EXEC CICS DUMP DUMPCODE("ERRS"); /* Cause a Transaction dump */
strcpy(messages,"TRANSACTION TERMINATED");
smnu(); /* There is no return from smnu() */
} /* end of errors() */
/* This function causes the main menu to be display on the screen */
void smnu(void)
{
memset(&sascaga,'\0',SASCAGAE); /* Clear Map A */
sascaga.msga = DFHBMASB; /* Set the message attribute byte */
memcpy(&sascaga.msgo,&messages,strlen(messages)); /* Move the message */
EXEC CICS SEND MAP("SASCAGA") ERASE; /* Send the map */
EXEC CICS RETURN; /* and return to CICS */
} /* end of smnu() */
|