www.sas.com > Service and Support > Technical Support
 
Technical Support SAS - The power to know(tm)
  TS Home | Intro to Services | News and Info | Contact TS | Site Map | FAQ | Feedback


/*---------------------------------------------------------------------+
|               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() */

Copyright (c) 2000 SAS Institute Inc. All Rights Reserved.
Terms of Use & Legal Information | Privacy Statement