
/*
 * NAME:        abt_emp.c - Sample implementation of EMP interface functions
 * VERSION:     1.0.0.0
 * DATE:        27 Jan 99
 * COPYRIGHT:
 *
 *   (C) COPYRIGHT International Business Machines Corp. 1993, 2000
 *   All Rights Reserved
 *   Licensed Materials - Property of IBM
 *
 *   US Government Users Restricted Rights - Use, duplication or
 *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 * This is a modification of the original user exit program provided
 * with CICS 4.0 (cics_emp.c). This program takes the 'abt_cics.emp'
 * file and reads in the table to set up Monitoring Points similar to
 * those found on MVS.  This program is capable of reading and storing
 * 20 Entry Names with 5 Trace ID's for each giving 100 Monitoring Points.
 * the 'abt_cics.emp' file must be a table with the following format
 *
 * ENTRYNME ID# FUNCTION C#
 *
 * A new line character must be placed immediately after the last number of each line
 *
 * Where the ENTRYNME is an arbitrary EntryName up to 8 characters in length,
 * the ID# is the TRace ID, the FUNCTION is the Function of the
 * Monitoring Point, ex. STOP, and the C# is the CLOCK or COUNTER number(1-5)
 * associated with the Monitoring Point.  A sample table looks like this:
 *
  TSTCASE1 1 START    1
  TSTCASE1 2 STOP     1
  TSTCASE1 3 WRITE    1
  XYZ      6 INCRMENT 2
  XYZ      7 DECRMENT 2
  XYZ      8 START    2
  XYZ      9 STOP     2
 *
 * The Entry Name is arbitrary and can be up to eight characters in length
 ************************************************************************************
 * The Entry Name must either be eight characters in length or filled with blank    *
 * spaces if shorter, because CICS will pass in eight characters regardless of size,*
 * if the name is less than eight characters, CICS fills the name with blank spaces *
 ************************************************************************************
 *
 * The ID# is arbitrary but only five ID's can be associated with an
 * Entry Name
 *
 * The only supported Functions with this program are
 *    START - Start a clock
 *    STOP  - Stop a clock
 *    INCRMENT - Increment a Counter
 *    DECRMENT - Decrement a Counter
 *    WRITE - Write a Monitoring Record
 *
 * There are only five Clocks and Counters that are available so the last
 * number must be (1-5)
 *
 * The Table needs to be placed in a text file named "abt_cics.emp" and placed into the
 * "C:\var\cics_regions\(REGION_NAME)\dumps\dir1" directory
 * The drive letter is whatever drive CICS is installed on and the REGION_NAME is the name
 * of the region which is using the user exit routine
 *
 * The Monitoring Record can be formatted by use of the accompaning formatter
 * program, abtmfmt.
 *
 *
 * SYNOPSIS:
 *
 * #include <cicstype.h>
 * #include <cics_emp.h>
 *
 * CICS_EMP_Return_t
 * CICS_EMP_Init        (IN    struct CICS_EMP_RegionData       *RegionData,
 *                       INOUT struct CICS_EMP_Functions        *Functions,
 *                       INOUT void                             **SaveSpace,
 *                       OUT   int                              *ErrorCode,
 *                       IN    struct CICS_EMP_PerformanceClass *SystemData,
 *                       IN    struct cics_eib                  *EIB,
 *                       IN    const struct CICS_EMP_Map        Dictionary[]);
 *
 * CICS_EMP_Return_t
 * CICS_EMP_EnterAPI    (IN    struct CICS_EMP_ENTER_API_Data   *EnterData,
 *                       INOUT void                             **SaveSpace,
 *                       OUT   int                              *ErrorCode);
 *
 * CICS_EMP_Return_t
 * CICS_EMP_WriteRecord (OUT   void                             **UserData,
 *                       OUT   int                              *UserDataLength,
 *                       IN    struct CICS_EMP_PerformanceClass *SystemData,
 *                       IN    int                              UnSupportedFields[],
 *                       INOUT void                             **SaveSpace,
 *                       OUT   int                              *ErrorCode,
 *                       IN    const struct CICS_EMP_Map        Dictionary[]);
 *
 * CICS_EMP_Return_t
 * CICS_EMP_ReStart     (IN    enum CICS_EMP_Finish             Action,
 *                       INOUT void                             **SaveSpace,
 *                       OUT   int                              *ErrorCode);
 *
 * void *CICS_EMP_Lookup(IN  int                              FID,
 *                       IN  const struct CICS_EMP_Map        Dictionary[],
 *                       IN  struct CICS_EMP_PerformanceClass *SystemData,
 *                       OUT int                              *Mask);
 * DESCRIPTION:
 *
 * These functions define the interface to the User EMP module.
 *
 * :hp3.CICS_EMP_Init:ehp3.
 *
 * This is the main entry point to the User EMP module.  It is invoked at the
 * start of each task if monitoring is enabled.  It performs three functions:
 * :ol.
 * :li.Determines if monitoring is required.
 * :li.Initialises any monitoring data used by the other User EMP
 * functions.
 * :li.If monitoring is required, it returns the address of those functions
 * to the caller in the :hp2.Functions:ehp2 parameter.
 * :eol.
 *
 * The :hp2.RegionData:ehp2. parameter can be used to log information
 * about the region from which this function was called, since the same
 * ULM may be used by many CICS regions.
 *
 * The parameter :hp2.SaveSpace:ehp2. allows the initialisation function to
 * pass the address of its private data area to the other ULM functions
 * (since static data cannot be used inside the ULM).  Normally, this space
 * will be :hp4.malloc'd:ehp4. during initialisation and :hp4.free'd:ehp4.
 * on task termination.  The returned address of this data is saved in the
 * Monitoring TCA structure for the task.  A copy of this value is passed
 * to the other ULM functions, which can be updated.  The new value (or old
 * value if not changed) is copied back to the TCA entry on completion of
 * the ULM function.
 *
 * The :hp2.ErrorCode:ehp2. parameter allows the function to return an error
 * code which is written as part of the log message when an return code other
 * than :hp4.CICS_EMP_RETURN_OK:ehp4. is returned.
 *
 * :hp3.CICS_EMP_EnterAPI:ehp3.
 *
 * This function processes the EXEC CICS ENTER API function.  The
 * information from the EXEC CICS ENTER API parameters is passed in the
 * :hp2.EnterData:ehp2. parameter.  The functionality of the entry point
 * is entirely user defined.  The function returns the appropriate
 * instructions to the calling function on completion.
 *
 * The :hp2.SaveSpace:ehp2. parameter contains the address of the ULM's
 * private data for this task.  The :hp2.ErrorCode:ehp2. parameter allows
 * the function to return an error code which is written as part of the
 * log message when an return code other than :hp4.CICS_EMP_RETURN_OK:ehp4.
 * or :hp4.CICS_EMP_RETURN_WRITE:ehp4. is returned.
 *
 * :hp3.CICS_EMP_WriteRecord:ehp3.
 *
 * This function is called to return the User Data buffer to be appended to
 * a monitoring record.  If the returned buffer address is NULL or the
 * length is zero, no user monitoring data is appended to the system
 * information.  The functionality of this entry is entirely user defined.
 * However, it should include stopping clocks if appropriate.
 *
 * The :hp2.SaveSpace:ehp2. parameter contains the address of the ULM's
 * private data for this task.  The :hp2.ErrorCode:ehp2. parameter allows
 * the function to return an error code which is written as part of the
 * log message when an return code other than :hp4.CICS_EMP_RETURN_OK:ehp4.
 * is returned.
 *
 * :hp2.UnSupportedFields:ehp2. is a null-delimited array of
 * CICS_EMP_PerformanceClass record field numbers. Each
 * entry in this array corresponds to an information field in the
 * CICS_EMP_PerformanceClass record which is not supported in the current
 * system environment. If this array is empty, then all fields are
 * supported. This array allows the user routine to distinguish between
 * a field which contains zero because no data has been collected and a
 * field which contains zero becuase it is not supported.
 *
 * :hp3.CICS_EMP_ReStart:ehp3.
 *
 * This function is called either when a monitoring record has been
 * successfully written to allow the restart of user clocks etc, or
 * when monitoring is complete for a task (either at the end of the
 * task or &infev. has detected an error) to allow the user functions
 * to release any resources acquired by the :hp2.CICS_EMP_Init:ehp2.
 * function.
 *
 * The :hp2.SaveSpace:ehp2. parameter contains the address of the ULM's
 * private data for this task.  The :hp2.ErrorCode:ehp2. parameter allows
 * the function to return an error code which is written as part of the
 * log message when an return code other than :hp4.CICS_EMP_RETURN_OK:ehp4.
 * is returned.
 *
 * :hp3.CICS_EMP_Lookup:ehp3.
 *
 * This function allows monitoring programs to access the SystemData without
 * needing to be recompiled for each release of CICS. It does this by using
 * a Dictionary provided as a parameter on the call to CICS_EMP_Init and
 * CICS_EMP_WriteRecord.
 *
 * This looks up the required CICS_EMP_FID value in the dictionary to
 * find its offset in the SystemData and then returns the address of
 * the item in the SystemData. It will return NULL if no match of the FID is
 * made.
 *
 * CAVEATS/WARNINGS:
 * The CICS shared region must be detached before calling these
 * functions. Any data stored in the shared region which is required by
 * these functions must be copied into local memory before being passed to
 * the User EMP functions.
 *
 * If a user function returns either :hp2.CICS_EMP_RETURN_FAIL:ehp2. or
 * :hp2.CICS_EMP_RETURN_DISABLE:ehp2., the user function is assumed to
 * have released any resources required and :hp2.CICS_EMP_ReStart:ehp2. will
 * not be invoked to release any resources.
 *
 * The pointer passed in the :hp2.SaveSpace:ehp2. parameter must be used
 * consistently between all four of the ULM functions otherwise pointer
 * alignment problems may arise.
 *
 * This code provides examples of the functionality that can be provided
 * via the ULM.
 *
 * RETURNED VALUES:
 *
 * :hp3.CICS_EMP_Init:ehp3.
 * :ul.
 * :li.
 * :hp2.CICS_EMP_RETURN_OK:ehp2. - The initialisation completed successfully.
 * :li.
 * :hp2.CICS_EMP_RETURN_FAIL:ehp2. - The initialisation detected an error.
 * User EMP are disabled for this region.
 * :li.
 * :hp2.CICS_EMP_RETURN_DISABLE:ehp2. - The initialisation completed
 * successfully.  However, monitoring is to be disabled for this
 * task.
 * :eul.
 * If :hp2.CICS_EMP_RETURN_OK:ehp2. is returned, the address of the
 * :hp4.CICS_EMP_EnterAPI:ehp4., :hp4.CICS_EMP_WriteRecord:ehp4. and
 * :hp4.CICS_EMP_ReStart:ehp4.  functions are returned in the
 * :hp2.Functions:ehp2. parameter.  In all other cases, the return value
 * of this parameter is ignored.
 *
 * :hp3.CICS_EMP_EnterAPI:ehp3.
 * :ul.
 * :li.
 * :hp2.CICS_EMP_RETURN_OK:ehp2. - The function completed successfully.
 * :li.
 * :hp2.CICS_EMP_RETURN_FAIL:ehp2. - The function detected an error.
 * User EMP are disabled for this task.
 * :li.
 * :hp2.CICS_EMP_RETURN_DISABLE:ehp2. - The function completed
 * successfully.  However, monitoring is to be disabled
 * for this task.
 * :li.
 * :hp2.CICS_EMP_RETURN_WRITE:ehp2. - The function completed successfully.
 * However, a monitoring record is to be written at this point.
 * This return value results in a call to :hp4.CICS_EMP_WriteRecord:ehp4.
 * to get the user data record.
 * :eul.
 *
 * :hp3.CICS_EMP_WriteRecord:ehp3.
 * :ul.
 * :li.
 * :hp2.CICS_EMP_RETURN_OK:ehp2. - The function completed successfully.
 * :li.
 * :hp2.CICS_EMP_RETURN_FAIL:ehp2. - The function detected an error.
 * User EMP are disabled for this task and no user data is written.
 * :li.
 * :hp2.CICS_EMP_RETURN_DISABLE:ehp2. - The function completed successfully.
 * However, no monitoring record is to be written at this point.
 * :eul.
 * If :hp2.CICS_EMP_RETURN_OK:ehp2. is returned, the User Data record and
 * its length are returned in the :hp2.UserData:ehp2. and
 * :hp2.UserDataLength:ehp2. parameter.  If either of these parameters is
 * NULL, only the system monitoring data is written to the file.  In all
 * other cases, the return value of these parameters is ignored.
 *
 * :hp3.CICS_EMP_ReStart:ehp3.
 * :ul.
 * :li.
 * :hp2.CICS_EMP_RETURN_OK:ehp2. - The function completed successfully.
 * :li.
 * :hp2.CICS_EMP_RETURN_FAIL:ehp2. - The function detected an error.
 * User EMP are disabled for this task.
 * :li.
 * :hp2.CICS_EMP_RETURN_DISABLE:ehp2. - The function completed successfully.
 * However, User EMP are disabled for this task.
 * :eul.
 *
 */


#include <cicstype.h>           /* CICS-supplied datatypes */
#include <cics_samples.h>       /* CICS-supplied macro definitions */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "abt_emp.h"
#include "abt_tdq.h"                                                   /*13171*/

void *CICS_EMP_Lookup( IN  int                              FID,
		       IN  const struct CICS_EMP_Map        Dictionary[],
		       IN  struct CICS_EMP_PerformanceClass *SystemData,
		       OUT int                              *Mask);

void GetNamesAndIDs( void * DataSpace);

/*
 * Define the data structures for the ULM save area.
 */

struct CICS_EMP_DataArea
{
	struct EMP					Emps[20];		/* Monitoring Points	*/
    struct CICS_EMP_Record      Record;         /* The record           */
	int                         WriteFlag;      /* Write before restart */
};

/*
 * The DataArea contains the Monitoring Points that are parsed from the file
 * and the record that is passed to CICS for writing to the Monitoring TDQ
 */

/*
 * Initialization function
 */
CICS_EMP_Return_t
CICS_EMP_Init          (IN    struct CICS_EMP_RegionData       *RegionData,
			INOUT struct CICS_EMP_Functions        *Functions,
			INOUT void                             **SaveSpace,
			OUT   int                              *ErrorCode,
			IN    struct CICS_EMP_PerformanceClass *SystemData,
			IN    struct cics_eib                  *EIB,
			IN    const struct CICS_EMP_Map        Dictionary[])
{
    /*ARGSUSED*/
    void        *DataSpace;             /* Our data space               */
    int c, i, j, k, l;
    char temp[9];
    struct CICS_EMP_DataArea    *LocalData;
    FILE *file;							/* The input file				*/

/* Get some space */

    DataSpace = (void *)malloc(sizeof (struct CICS_EMP_DataArea));
    memset(DataSpace, 0, sizeof (struct CICS_EMP_DataArea));

	memset(temp, 0, 8);
/* Check to see if we got it */

    if (DataSpace == NULL)
    {
	*ErrorCode = 1;
	*SaveSpace = NULL;
	return CICS_EMP_RETURN_FAIL;
    }

    ((struct CICS_EMP_DataArea *)DataSpace)->WriteFlag = 1;

    *SaveSpace = DataSpace;
    Functions->CICS_EMP_EnterAPI        = CICS_EMP_EnterAPI;
    Functions->CICS_EMP_WriteRecord     = CICS_EMP_WriteRecord;
    Functions->CICS_EMP_ReStart         = CICS_EMP_ReStart;

/*
 * User Code to emulate MVS Monitor points
 * The ENTRYNAME's and TRACEID's along with functionality are entered into a file
 * that is parsed and stored in the DataSpace
 */

    file = fopen("abt_cics.emp", "r");
    LocalData = (struct CICS_EMP_DataArea *)*SaveSpace;

/*
 * The file is opened and read into the DataSpace up to Twenty EntryNames.
 * Each Entry Name can have up to ten TraceIds with different functions
 */

    for (l = 0; l < 8; l++)
	{
		c = fgetc(file);
	    temp[l] = (char)c;
	}

	for (i = 0; i < 20; i++)
    {
	/*
	 * The Entry Name for the Monitoring Point is stored
	 * in the DataArea and the record
	 */

		strcpy(LocalData->Emps[i].EntryName, temp);
		strcpy(LocalData->Record.RecordEMPs[i].EntryName, temp);
		
	/*
	 * Loop five times to get the five possible Trace ID's for the EntryName
	 * If a different Entry Name is found, the loop is terminated by
	 * setting j = 5.
	 */

	/*
	 * To increase the number of Trace Id's, change the structure found in 'abt_emp.h'
	 * and the loop test condition
	 */

		for (j = 0; j < 5; j++)
		{
			c = fgetc(file); /* Get the Blank Space */
			while (c == ' ')
				c = fgetc(file); /* Get the first number of the Trace ID */
			LocalData->Emps[i].ids[j].TraceId = c - ((unsigned int)'0');

			c = fgetc(file);
		/*
		 * If there are more than 1 digit in the TraceID convert all the digits
		 * to integer
		 */
			if (c != ' ')
			{
				LocalData->Emps[i].ids[j].TraceId = LocalData->Emps[i].ids[j].TraceId * 10;
				LocalData->Emps[i].ids[j].TraceId += c - ((unsigned int)'0');
				c = fgetc(file);
			}

		/*  Get the function of the Monitoring Point  */
		/*	fgets(temp, 9, file);   */
		    for (l = 0; l < 8; l++)
			{
				c = fgetc(file);
				temp[l] = (char)c;
			    if (c == ' ')
				{
					temp[l] = 0;
					l = 8;
				}
			}

			strcpy(LocalData->Emps[i].ids[j].Function, temp);
			c = fgetc(file);
			while(c == ' ')
				c = fgetc(file);
			LocalData->Emps[i].ids[j].CNumber = c - ((unsigned int)'0');
			c = fgetc(file);

		/*  Zero out the temp storage for the next string  */
			for(k = 0; k < 9; k++)
				temp[k] = 0;

		/*  Get the next Entry Name and check for End of File  */	
			for (l = 0; l < 8; l++)
			{
			    c = fgetc(file);

			/*
			 * If the End of File is found, the number of Entry Points read
			 * in is stored in the record
			 */
				if (feof(file))
				{
					LocalData->Record.NumberOfEmps = i + 1;
					i = 20;
					j = 5;
				}
				else
				{
					temp[l] = (char)c;
				}
			}

		/*
		 * Compare the temp string with the currnet EntryName to determine
		 * whether the next row in the table is the same Entry Name or another
		 * If it is different the loop is terminated and a new loop is started
		 * for the new Entry Name
		 */
			if (strncmp(temp, LocalData->Emps[i].EntryName, 8) != 0)
			    j = 5;
		}
    }
    fclose(file);

    return CICS_EMP_RETURN_OK;
}


/*
 * EXEC CICS ENTER API Processing For MVS style
 */

CICS_EMP_Return_t
CICS_EMP_EnterAPI       (IN    struct CICS_EMP_ENTER_API_Data   *EnterData,
			 INOUT void                             **SaveSpace,
			 OUT   int                              *ErrorCode)
{
    int i = 0;
	int j = 0;
    char temp[10];
    struct CICS_EMP_DataArea    *LocalData;

    LocalData = (struct CICS_EMP_DataArea *)*SaveSpace;

/* Check initialisation OK */

    if (*SaveSpace == NULL)
    {
	*ErrorCode = 1;
	return CICS_EMP_RETURN_FAIL;
    }

    LocalData->WriteFlag = 0;

/* Find the Entry Name in the table */
    for (j = 0; j < 20; j++)
    {
		if (strncmp(EnterData->EntryName, LocalData->Emps[j].EntryName, 8) == 0)
		    j = 20;
		else
			i++;
	}

/* Find the Trace ID associated with the Entry Name */
    for (j = 0; j < 5; j++)
    {
	/*
	 * If the Trace ID is found, the loop switches on the Clock or Counter number
	 * associated with the Entry Name and Trace ID.  Once the Clock/Counter Number is
	 * found, each case determines the function of the Monitoring Point and does
	 * the appropriate operation.
	 *
	 * If the Trace ID is not found, a CICS_EMP_RETURN_FAIL will be returned at
	 * end of the function.
	 */
		if (EnterData->ID == LocalData->Emps[i].ids[j].TraceId)
		{
			switch(LocalData->Emps[i].ids[j].CNumber)
			{
		/* Clock and Counter 1 */
			case 1:
				if (strncmp("START", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Start the Clock */
				    LocalData->Emps[i].Active1 = 1;
					CICS_EMP_StartClock (&(LocalData->Emps[i].Clock1),
						 CICS_EMP_CLOCK_TYPE_ELAPSED);
					LocalData->Record.RecordEMPs[i].Clock1Used = 1;
					return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("STOP", LocalData->Emps[i].ids[j].Function, 4) == 0)
				{ /* Stop the Clock */
					LocalData->Emps[i].Active1 = 0;
				    CICS_EMP_StopClock (&(LocalData->Emps[i].Clock1));
					LocalData->Record.RecordEMPs[i].Clock1Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("INCRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Increment counter */
					(LocalData->Emps[i].Counter1)++;
					LocalData->Record.RecordEMPs[i].Counter1Used = 1;
					return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("DECRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Decrement Counter */
					(LocalData->Emps[i].Counter1)--;
					LocalData->Record.RecordEMPs[i].Counter1Used = 1;
					return CICS_EMP_RETURN_OK;
				}	
				else if (strncmp("WRITE", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Write a Monitoring Record */
					return CICS_EMP_RETURN_WRITE;
				}
				break;
		/* Clock and Counter 2 */
		    case 2:
				if (strncmp("START", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Start the Clock */
					LocalData->Emps[i].Active2 = 1;
					CICS_EMP_StartClock (&(LocalData->Emps[i].Clock2),
						 CICS_EMP_CLOCK_TYPE_ELAPSED);
					LocalData->Record.RecordEMPs[i].Clock2Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("STOP", LocalData->Emps[i].ids[j].Function, 4) == 0)
				{ /* Stop the Clock */
				    LocalData->Emps[i].Active2 = 0;
					CICS_EMP_StopClock (&(LocalData->Emps[i].Clock2));
					LocalData->Record.RecordEMPs[i].Clock2Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("INCRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Increment counter */
				    (LocalData->Emps[i].Counter2)++;
					LocalData->Record.RecordEMPs[i].Counter2Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("DECRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Decrement Counter */
					(LocalData->Emps[i].Counter2)--;
					LocalData->Record.RecordEMPs[i].Counter2Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("WRITE", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Write a Monitoring Record */
					return CICS_EMP_RETURN_WRITE;
				}
				break;
		/* Clock and Counter 3 */
		    case 3:
				if (strncmp("START", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Start the Clock */
				    LocalData->Emps[i].Active3 = 1;
					CICS_EMP_StartClock (&(LocalData->Emps[i].Clock3),
						 CICS_EMP_CLOCK_TYPE_ELAPSED);
					LocalData->Record.RecordEMPs[i].Clock3Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("STOP", LocalData->Emps[i].ids[j].Function, 4) == 0)
				{ /* Stop the Clock */
				    LocalData->Emps[i].Active3 = 0;
					CICS_EMP_StopClock (&(LocalData->Emps[i].Clock3));
					LocalData->Record.RecordEMPs[i].Clock3Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("INCRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Increment counter */
				    (LocalData->Emps[i].Counter3)++;
					LocalData->Record.RecordEMPs[i].Counter3Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("DECRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Decrement Counter */
					(LocalData->Emps[i].Counter3)--;
					LocalData->Record.RecordEMPs[i].Counter3Used = 1;
					return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("WRITE", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Write a Monitoring Record */
					return CICS_EMP_RETURN_WRITE;
				}
				break;
		/* Clock and Counter 4 */
		    case 4:
				if (strncmp("START", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Start the Clock */
					LocalData->Emps[i].Active4 = 1;
					CICS_EMP_StartClock (&(LocalData->Emps[i].Clock4),
						 CICS_EMP_CLOCK_TYPE_ELAPSED);
					LocalData->Record.RecordEMPs[i].Clock4Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("INCRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Stop the Clock */
				    LocalData->Emps[i].Active4 = 0;
				    CICS_EMP_StopClock (&(LocalData->Emps[i].Clock4));
					LocalData->Record.RecordEMPs[i].Clock4Used = 1;
					return CICS_EMP_RETURN_OK;
				}
				else if (strcmp("INCRMENT", LocalData->Emps[i].ids[j].Function) == 0)
				{ /* Increment counter */
				    (LocalData->Emps[i].Counter4)++;
					LocalData->Record.RecordEMPs[i].Counter4Used = 1;
					return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("DECRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Decrement Counter */
					(LocalData->Emps[i].Counter4)--;
					LocalData->Record.RecordEMPs[i].Counter4Used = 1;
					return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("WRITE", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Write a Monitoring Record */
					return CICS_EMP_RETURN_WRITE;
				}	
				break;
		/* Clock and Counter 5 */
			case 5:
				if (strncmp("START", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Start the Clock */
				    LocalData->Emps[i].Active5 = 1;
				    CICS_EMP_StartClock (&(LocalData->Emps[i].Clock5),
						 CICS_EMP_CLOCK_TYPE_ELAPSED);
					LocalData->Record.RecordEMPs[i].Clock5Used = 1;
					return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("STOP", LocalData->Emps[i].ids[j].Function, 4) == 0)
				{ /* Stop the Clock */
				    LocalData->Emps[i].Active5 = 0;
					CICS_EMP_StopClock (&(LocalData->Emps[i].Clock5));
					LocalData->Record.RecordEMPs[i].Clock5Used = 1;
					return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("INCRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Increment counter */
					(LocalData->Emps[i].Counter5)++;
					LocalData->Record.RecordEMPs[i].Counter5Used = 1;
					return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("DECRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Decrement Counter */
					(LocalData->Emps[i].Counter5)--;
					LocalData->Record.RecordEMPs[i].Counter5Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("WRITE", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Write a Monitoring Record */
					return CICS_EMP_RETURN_WRITE;
				}
				break;
		/* The default Clock and Counter is One */
			default:
				if (strncmp("START", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Start the Clock */
				    LocalData->Emps[i].Active1 = 1;
				    CICS_EMP_StartClock (&(LocalData->Emps[i].Clock1),
						 CICS_EMP_CLOCK_TYPE_ELAPSED);
					LocalData->Record.RecordEMPs[i].Clock1Used = 1;
					return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("STOP", LocalData->Emps[i].ids[j].Function, 4) == 0)
				{ /* Stop the Clock */
				    LocalData->Emps[i].Active1 = 0;
					CICS_EMP_StopClock (&(LocalData->Emps[i].Clock1));
					LocalData->Record.RecordEMPs[i].Clock1Used = 1;
					return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("INCRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Increment counter */
					(LocalData->Emps[i].Counter1)++;
					LocalData->Record.RecordEMPs[i].Counter1Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("DECRMENT", LocalData->Emps[i].ids[j].Function, 8) == 0)
				{ /* Decrement Counter */
					(LocalData->Emps[i].Counter1)--;
					LocalData->Record.RecordEMPs[i].Counter1Used = 1;
				    return CICS_EMP_RETURN_OK;
				}
				else if (strncmp("WRITE", LocalData->Emps[i].ids[j].Function, 5) == 0)
				{ /* Write a Monitoring Record */
					return CICS_EMP_RETURN_WRITE;
				}
				break;
			}
		/*
		 * If for some reason the default does not catch the error, the loop
		 * is terminated and the CICS_EMP_RETURN_FAIL is returned
		 */
			j = 5;
		}
	}
    return CICS_EMP_RETURN_FAIL;
}

/*
 * Write Record function
 */

CICS_EMP_Return_t
CICS_EMP_WriteRecord    (OUT   void                      **UserData,
			 OUT   int                       *UserDataLength,
			 IN    struct
			       CICS_EMP_PerformanceClass *SystemData,
			 IN    int                       UnSupportedFields[],
			 INOUT void                      **SaveSpace,
			 OUT   int                       *ErrorCode,
			 IN    const struct CICS_EMP_Map Dictionary[])
{
    struct CICS_EMP_DataArea    *LocalData;
    int i;		/* loop counter */
	int j;		/* for loop counter */
    cics_char_t * RecordType;
    cics_char_t * UserID;

    RecordType = (cics_char_t *)CICS_EMP_Lookup(CICS_EMP_FID_RECORD_TYPE,
					 Dictionary, SystemData, NULL);

    UserID     = (cics_char_t *)CICS_EMP_Lookup(CICS_EMP_FID_USER_ID,
					 Dictionary, SystemData, NULL);

    LocalData = (struct CICS_EMP_DataArea *)*SaveSpace;

/* Flag that the record has been written */

	LocalData->WriteFlag = 1;

/* Disable monitoring if we are not a ULM or end of task record */

    if ((*RecordType != CICS_EMP_REC_TYPE_ULM) &&
		(*RecordType != CICS_EMP_REC_TYPE_END))
    {
		free(LocalData);

		*ErrorCode = 4;
		return CICS_EMP_RETURN_DISABLE;
    }

/* If we are root or the data area does not exist - disable ULM */

    else if ((strncmp(UserID, "root", 4) == 0) || (*SaveSpace == NULL))
    {

/* Free the memory we acquired */

		if (*SaveSpace != NULL)
		{
		    free(LocalData);
		}

		*ErrorCode = 5;
		*(UserData) = NULL;
		*UserDataLength = 0;
		return CICS_EMP_RETURN_FAIL;
	}

/* Verify that required fields in the PerformanceClass record are available */

    i = 0;
    while(UnSupportedFields[i] != 0)
    {

/* We are only interested in the swap count field, so if it is not      */
/* available clean up and quit.                                         */

		if(UnSupportedFields[i] == CICS_EMP_FID_SWAP_COUNT)
		{

/* Free the memory we acquired */

			if (*SaveSpace != NULL)
			{
				free(LocalData);
		    }

			*ErrorCode = 7;
			*(UserData) = NULL;
		    *UserDataLength = 0;
		    return CICS_EMP_RETURN_FAIL;
		}
    }

/* Set up to return our record.  Stop all the clocks and set up the record */

	for (j = 0; j < 20; j++)
	{
	    CICS_EMP_StopClock (&(LocalData->Emps[i].Clock1));
	    CICS_EMP_StopClock (&(LocalData->Emps[i].Clock2));
	    CICS_EMP_StopClock (&(LocalData->Emps[i].Clock3));
	    CICS_EMP_StopClock (&(LocalData->Emps[i].Clock4));
	    CICS_EMP_StopClock (&(LocalData->Emps[i].Clock5));
	}

/*
 * The only information passed to CICS in the record is that of Entry Names
 * and the Clocks and Counters that were used during monitoring.
 */
	for (j = 0; j < LocalData->Record.NumberOfEmps; j++)
	{
		if (LocalData->Record.RecordEMPs[i].Clock1Used)
		{
			LocalData->Record.RecordEMPs[i].Clk_Counter1 =
					LocalData->Emps[i].Clock1.Clock.Counter;
			LocalData->Record.RecordEMPs[i].Clk_Time1 =
					LocalData->Emps[i].Clock1.Clock.TotalTime;
		}
		if (LocalData->Record.RecordEMPs[i].Clock2Used)
		{
			LocalData->Record.RecordEMPs[i].Clk_Counter2 =
					LocalData->Emps[i].Clock2.Clock.Counter;
			LocalData->Record.RecordEMPs[i].Clk_Time2 =
					LocalData->Emps[i].Clock2.Clock.TotalTime;
		}
		if (LocalData->Record.RecordEMPs[i].Clock3Used)
		{
			LocalData->Record.RecordEMPs[i].Clk_Counter3 =
					LocalData->Emps[i].Clock3.Clock.Counter;
			LocalData->Record.RecordEMPs[i].Clk_Time3 =
					LocalData->Emps[i].Clock3.Clock.TotalTime;
		}
		if (LocalData->Record.RecordEMPs[i].Clock4Used)
		{
			LocalData->Record.RecordEMPs[i].Clk_Counter4 =
					LocalData->Emps[i].Clock4.Clock.Counter;
			LocalData->Record.RecordEMPs[i].Clk_Time4 =
					LocalData->Emps[i].Clock4.Clock.TotalTime;
		}
		if (LocalData->Record.RecordEMPs[i].Clock5Used)
		{
			LocalData->Record.RecordEMPs[i].Clk_Counter5 =
					LocalData->Emps[i].Clock5.Clock.Counter;
			LocalData->Record.RecordEMPs[i].Clk_Time5 =
					LocalData->Emps[i].Clock5.Clock.TotalTime;
		}
		if (LocalData->Record.RecordEMPs[i].Counter1Used)
			LocalData->Record.RecordEMPs[i].Counter1r = LocalData->Emps[i].Counter1;
		if (LocalData->Record.RecordEMPs[i].Counter2Used)
			LocalData->Record.RecordEMPs[i].Counter2r = LocalData->Emps[i].Counter2;
		if (LocalData->Record.RecordEMPs[i].Counter3Used)
			LocalData->Record.RecordEMPs[i].Counter3r = LocalData->Emps[i].Counter3;
		if (LocalData->Record.RecordEMPs[i].Counter4Used)
			LocalData->Record.RecordEMPs[i].Counter4r = LocalData->Emps[i].Counter4;
		if (LocalData->Record.RecordEMPs[i].Counter5Used)
			LocalData->Record.RecordEMPs[i].Counter5r = LocalData->Emps[i].Counter5;
	}

/* Return the record address and length */

    *(UserData) = (void *)&(LocalData->Record);
    *UserDataLength = sizeof(struct CICS_EMP_Record);
    return CICS_EMP_RETURN_OK;
}

/*
 * Cleanup and Restart
 */

CICS_EMP_Return_t
CICS_EMP_ReStart        (IN    enum CICS_EMP_Finish Action,
			 INOUT void                 **SaveSpace,
			 OUT   int                  *ErrorCode)
{
	int i;
    struct CICS_EMP_DataArea    *LocalData;

    LocalData = (struct CICS_EMP_DataArea *)*SaveSpace;

/* Check initialisation OK */

    if (*SaveSpace == NULL)
    {
	*ErrorCode = 6;
	return CICS_EMP_RETURN_FAIL;
    }

/* Check to see of the data has been written before the restart */

	if (LocalData->WriteFlag == 0)
	{
		return CICS_EMP_RETURN_WRITE;
	}

/* Decide what to do based on what we are asked to do */

    switch (Action)                     /* Switch on action             */
    {

/* Just written a record - restart our clocks and reset the counters */

	case CICS_EMP_FINISH_RESTART:
		LocalData->WriteFlag = 0;
		for (i = 0; i < 20; i++)
		{
			LocalData->Emps[i].Counter1 = 0;
			LocalData->Emps[i].Counter2 = 0;
		    LocalData->Emps[i].Counter3 = 0;
		    LocalData->Emps[i].Counter4 = 0;
			LocalData->Emps[i].Counter5 = 0;


/* Restart clock 1 if it is active */

		    if (LocalData->Emps[i].Active1);
		    {
				CICS_EMP_StartClock (&(LocalData->Emps[i].Clock1),
					     CICS_EMP_CLOCK_TYPE_ELAPSED);
				LocalData->Emps[i].Active1 = 0;
			}

/* Restart clock 2 if it is active */

		    if (LocalData->Emps[i].Active2);
		    {
				CICS_EMP_StartClock (&(LocalData->Emps[i].Clock2),
					     CICS_EMP_CLOCK_TYPE_ELAPSED);
				LocalData->Emps[i].Active2 = 0;
		    }

/* Restart clock 3 if it is active */

		    if (LocalData->Emps[i].Active3);
		    {
				CICS_EMP_StartClock (&(LocalData->Emps[i].Clock3),
					     CICS_EMP_CLOCK_TYPE_ELAPSED);
				LocalData->Emps[i].Active3 = 0;
		    }

/* Restart clock 4 if it is active */

		    if (LocalData->Emps[i].Active4);
		    {
				CICS_EMP_StartClock (&(LocalData->Emps[i].Clock4),
					     CICS_EMP_CLOCK_TYPE_ELAPSED);
				LocalData->Emps[i].Active4 = 0;
			}

/* Restart clock 5 if it is active */

		    if (LocalData->Emps[i].Active5);
		    {
				CICS_EMP_StartClock (&(LocalData->Emps[i].Clock5),
					     CICS_EMP_CLOCK_TYPE_ELAPSED);
				LocalData->Emps[i].Active5 = 0;
			}
		}

	    return CICS_EMP_RETURN_OK;

/* Clean up - Monitoring has detected some error, therefore release our
 * data space
 */

	case CICS_EMP_FINISH_CLEANUP:

	    free(LocalData);

	    return CICS_EMP_RETURN_OK;
    }

/* Unexpected code - just report it */

    *ErrorCode = Action;
    return CICS_EMP_RETURN_FAIL;
}

void *CICS_EMP_Lookup( IN  int                              FID,
		       IN  const struct CICS_EMP_Map        Dictionary[],
		       IN  struct CICS_EMP_PerformanceClass *SystemData,
		       OUT int                              *Mask)
{
    while ( ((Dictionary->Mask & CICS_EMP_FID_MASK) != FID) &&
	     (Dictionary->Mask != ~0) )
    {
	Dictionary++;
    }

    if (Dictionary->Mask == ~0)
    {
	if (Mask != NULL) *Mask = 0;
	return NULL;
    }
    else
    {
	if (Mask != NULL) *Mask = Dictionary->Mask;
	return (void *)( ((cics_char_t *)SystemData) + Dictionary->Offset );
    }
}
