/*---------------------------------------------------------------------+ | 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 /* Useful BMS definitions */ #include /* C struct defining Map A, produced by DSECT2C */ #include /* C struct defining Map B, produced by DSECT2C */ #include /* 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() */