
/*
 * NAME:        abtmfmt (main) - Format a Monitor Data File.
 * VERSION:     1.0
 *
 * 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 file is a modification of the original cicsmfmt.c file that is
 * provided with CICS 4.0.  This program was created to read the Monitoring
 * Record that is generated by the user exit program, 'abt_emp'.  This program
 * generates a report of the data accumulated through the region and the monitoring
 * points set up by the user to be used with the 'abt_emp' monitoring program.
 *
 * The output of this program is similar to that of the original program, cicsmfmt,
 * except that when a monitoring point is encountered with the transaction data, the
 * program outputs the clock time and counters that were used within the monitored
 * CICS program.
 *
 *
 * SYNOPSIS:
 *
 * abtmfmt [ -? ] <filename>
 *
 * DESCRIPTION:
 * The abtmfmt utility is invoked from the &os. shell to format data from
 * CICS Monitoring Transient Data Queue files.
 *
 * :hp2.cicsmfmt:ehp2. reads each record from the queue specified by the
 * :hp4.filename:ehp4. parameter and prints the following fields:
 * :ul compact.
 * :li.
 * The Transaction Name (Field ID: 1);
 * :li.
 * The Terminal Name (Field ID: 2).
 * :li.
 * The Start and End Timestamps for the task, reporting the number of
 * seconds with a 10 microsecond precision (Field IDs: 5 & 6);
 * :li.
 * The Time spent waiting for File I/O and the number of times the clock
 * was started/stopped. (Field ID: 63).
 * :li.
 * The Program Name (Field ID: 71).
 * :li.
 * The Total number of File Requests issued (Field ID: 93);
 * :li.
 * The Data Segment Memory Occupancy (Field ID: 95);
 * :li.
 * The first Abend Code if set (Field ID: 113);
 * :eul.
 *
 * At the end of the file, the total number of File I/O requests and total
 * time waiting for File I/O are printed.
 *
 * Help information is printed if the -? parameter is supplied or
 * when cicsmfmt is entered with the wrong syntax.
 *
 * CAVEATS/WARNINGS:
 * None.
 *
 * RETURNED VALUES:
 * :ul. compact.
 * :li.0 - Program completed successfully
 * :li.1 - Cannot open Monitoring Data file
 * :li.2 - Read error or unexpected EOF on Monitoring Data file
 * :li.3 - Invalid usage.
 * :li.4 - Invalid data from Monitoring Data file.
 * :eul.
 *
 * RELATED INFORMATION:
 * None.
 *
 */


#include <cicstype.h>                   /* CICS-supplied datatypes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <locale.h>

#include "abtmfmt.h"
#include "abt_msg.h"                                                   /*13171*/
#include "abt_emp.h"

/*
 * PRIVATE OBJECT: TitleItem (titem) - A printable title item
 */
struct CICS_MFMT_TitleItem
{
    int MessageNumber;
    cics_char_t *DefaultText;
    cics_char_t *PrintFormat;
};
/*
 * DESCRIPTION:
 * This structure describes each printable header field in the report.
 * :dl.
 * :dt.MessageNumber
 * :dd.The message number which corresponds to the text for the column
 * being printed.
 * :dt.DefaultText
 * :dd.The text to use if the message catalogue is not available.
 * :dt.PrintFormat
 * :dd.The format to use for printing the text.
 * :edl.
 */

/*
 * PRIVATE OBJECT: TitleLine (titles) - The specification of the page title
 */
#define CICS_MFMT_PAGE_WIDTH    150     /* in chars */
#define CICS_MFMT_PAGE_SIZE     60      /* in lines */
#define CICS_MFMT_DISPLAY_COLS  10      /* number of data columns in output */
#define CICS_MFMT_TITLE_LINES   3       /* number of lines in the title */
#define CICS_MFMT_MAX_HEADERS   4       /* number of lines in header */

/*
 * # defines for use during scaling of Occupancy (64-bit) values.
 */

#define Zero 0
#define OneK 1024
#define OneM 1024*1024
#define OneG 1024*1024*1024

struct CICS_MFMT_TitleItem
TitleLine[CICS_MFMT_TITLE_LINES][CICS_MFMT_DISPLAY_COLS] = {

    {{CICS_MFMT_SRH_TRANID,     "Transaction Id",       "%s"},
     {CICS_MFMT_SRH_TERMID,     "Terminal Id",          "  %s   "       },
     {CICS_MFMT_SRH_PROGID,     "Program Id",           "  %s  "        },
     {CICS_MFMT_SRH_START,      "Start Time",           "  %s  "        },
     {CICS_MFMT_SRH_END,        "End Time",             "    %s   "     },
     {CICS_MFMT_SRH_FILE,       "File I/O",             "          %s"  },
     {CICS_MFMT_NO_TEXT,        "",                     "          "    },
     {CICS_MFMT_SRH_DATA,       "Data Segment",         " %s "          },
     {CICS_MFMT_SRH_ABEND,      "First ABEND",          " %s"           },
	{ CICS_MFMT_NO_TEXT,		"",						"          "	}},

     /* header line 1 complete */

    {{CICS_MFMT_NO_TEXT,        "",                     "              "},
     {CICS_MFMT_NO_TEXT,        "",                     "              "},
     {CICS_MFMT_NO_TEXT,        "",                     "              "},
     {CICS_MFMT_NO_TEXT,        "",                     "              "},
     {CICS_MFMT_NO_TEXT,        "",                     "              "},
     {CICS_MFMT_SRH_REQUEST,    "Request",              "     %s  "     },
     {CICS_MFMT_SRH_ELAPSED,    "Elapsed Time",         "  %s "         },
     {CICS_MFMT_SRH_OCCUPANCY,  "Occupancy",            "     %s "      },
     {CICS_MFMT_SRH_CODE,       "Code",                 "      %s"      },
	{ CICS_MFMT_NO_TEXT,		"",						"        "		}},

     /* header line 2 complete */

	{{CICS_MFMT_ENTRYNAME,		"Entry Name",			"%s "			},
	{ CICS_MFMT_CLOCK1,			"Clock 1 Value",		" %s   "		},
	{ CICS_MFMT_CLOCK2,			"Clock 2 Value",		" %s   "		},
	{ CICS_MFMT_CLOCK3,			"Clock 3 Value",		" %s   "		},
	{ CICS_MFMT_CLOCK4,			"Clock 4 Value",		" %s   "		},
	{ CICS_MFMT_CLOCK5,			"Clock 5 Value",		" %s  "			},
	{ CICS_MFMT_COUNTERS,		"-1-   -2-   -3-",		"%s"			},
	{ CICS_MFMT_COUNTERS2,		"   -4-   -5-",			"%s"			},
	{ CICS_MFMT_NO_TEXT,		"",						" "				},
	{ CICS_MFMT_NO_TEXT,		"",						" "				}}};

	/* header line 3 complete */

/*
 * DESCRIPTION:
 * The definition of the Page Title.
 */

/*
 * The third line of the header is only printed when the monitoring point
 * is printed
 */

/*
 * PRIVATE OBJECT: PrintItem (ditem) - A printable data item
 */
struct CICS_MFMT_PrintItem
{
    int FieldID;
    cics_char_t *PrintFormat;
};
/*
 * DESCRIPTION:
 * This structure describes a printable data item.
 * :dl.
 * :dt.FieldID
 * :dd.The Field within the monitoring Data record.
 * :dt.PrintFormat
 * :dd.The printf style format string which is used to print the
 * data.
 * :edl.
 */

/*
 * PRIVATE OBJECT: Fields (fields) - Data specification
 */
struct CICS_MFMT_PrintItem Fields[CICS_MFMT_DISPLAY_COLS + 1] = {
        {1,     "     %s     "                  },
        {2,     "      %s     "                 },
        {71,    "    %s   "                     },
        {5,     " %.2d:%.2d:%.2d.%.4ld "        },
        {6,     " %.2d:%.2d:%.2d.%.4ld "        },
        {93,    " %7d "                         },
        {63,    "  %2d.%06d/%-5d"               },
        {95,    "  %12u%s"                      },
        {113,   "     %s "                      },
		{0,     ""								},
        {0,     "      -       "                }};
                                /* final entry is special. This format is
                                   used when the field id is not found
                                   to enable the output to be aligned */
/*
 * DESCRIPTION:
 * This array defines each printable row except that of the monitoring points from the ULM.
 * The information there is printed only when necessary in the necessary format.
 */


/*
 * Global variables for use between functions.
 */
cics_char_t PageHeader[CICS_MFMT_MAX_HEADERS][CICS_MFMT_PAGE_WIDTH + 1];
cics_char_t PageFooter[CICS_MFMT_PAGE_WIDTH + 1];
cics_ulong_t LineNo = 1;
cics_ulong_t PageNo = 1;
CICS_MFMT_Stream_t *DataStream;

/*
 * Description:
 *      PageHeader      - a variable which holds the fully formed header
 *                        for each page.
 *      PageFooter      - a variable which holds the fully formed footer
 *                        for each page.
 *      LineNo          - the current line number. Reset at page start.
 *      PageNo          - the current page number.
 *      DataStream      - pointer to a record which holds details of the
 *                        current input stream.
 */

/*
 * Function Prototypes.
 */
void NewLine(void);
void NewPage(void);
void FormatSetup(cics_char_t *DataFileName);
void OutputPageHeader(void);
void OutputPageFooter(void);

#ifdef _WIN32
#define CDECL _cdecl
#define DIR_SEPARATOR_CHAR '\\'
#else
#define CDECL
#define DIR_SEPARATOR_CHAR '/'
#endif

int CDECL main (int ArgC, cics_char_t **ArgV)
{
    int InvalidUsage = FALSE;           /* this executable invoked ok ? */
    int HelpQuery    = FALSE;           /* did the user ask for help ? */
    cics_char_t *OutputMessage;         /* ptr to current output message */
    enum CICS_MFMT_Status OpStatus;     /* error from attempt to open file */
    int Column;                         /* current data column being output */
    time_t Time;                        /* time in seconds */
    time_t Time2;                        /* time in seconds */
    time_t Time3;                        /* time in seconds */
    time_t Time4;                        /* time in seconds */
    time_t Time5;                        /* time in seconds */
    struct tm TimeData;                 /* current time processed */
    CICS_EMP_TDQ_RecordDetails_t *CurrentMonitorRec;
                                        /* ptr data for current print item */
    enum CICS_MFMT_Status InOutStatus = CICS_MFMT_STATUS_OK;
                                        /* status of i/o operations */

    cics_char_t StringBuffer[10];       /* Buffer for printing strings */
    cics_sshort_t i;                    /* Array Index                 */
    cics_sshort_t j;                    /* Array Index                 */
	cics_sshort_t header;				/* user header flag            */

    /*
     * The following fields are used in the scaling of Occupancy values.
     */
    int BitShift;                       /* Used during bit shifting    */
    int LSWMask = 0x80000000;           /* Least Sig. Word mask        */
    int MSWMask = 0x00000001;           /* Most Sig Word mask          */
    char AppendChar[2];                 /* Scaling char K,M,G or T     */

	cics_char_t Buffer[20];				/* Buffer for printing user data  */

    MFMT_DEBUG(("ArgC: %d\n", ArgC));


    setlocale(LC_ALL, "");

    /*
     * Ideally for testing the options supplied on the invocation
     * line, getopts should be used. However, the current interface
     * does not require such processing.
     */

    if ((ArgC == 1) || (ArgC > 2))
    {
        /*
         * Either no args or too many args supplied.
         */
        InvalidUsage = TRUE;
    }
    else
    {
        MFMT_DEBUG(("ArgV: [%s]\n", ArgV[1]));

        if (*(ArgV[1]) == '-')
        {
            InvalidUsage = TRUE;
            if (*(ArgV[1] + 1) == '?')
            {
                HelpQuery = TRUE;
            }
        }
    }

    /*
     * At this point, if InvalidUsage is FALSE then we know the
     * only parameter supplied does not begin with a '-'. Hence we
     * assume it to be the name of the Monitoring data file.
     */

    if (InvalidUsage)
    {
        MFMT_DEBUG(("Accessing message catalog\n"));
        OutputMessage = CICS_MFMT_GetMessage(ERZ065001E, CICS_MFMT_ERROR_SET);
        if (OutputMessage != NULL)
        {
            fprintf(stderr, OutputMessage, ArgV[0]);
            fprintf(stderr, "\n");
            CICS_MFMT_FreeMessage(OutputMessage);
            CICS_MFMT_ReleaseCatalogue();
        }
        if (HelpQuery)
        {
            exit(0);
        }
        else
        {
            exit(3);
        }
    }

    /*
     * Open the data file. Test for any resulting errors.
     */

    DataStream = CICS_MFMT_OpenMonitorFile(ArgV[1], &OpStatus);

    if (DataStream == (CICS_MFMT_Stream_t *)NULL)
    {
        MFMT_DEBUG(("Error opening monitor details file\n"));

        switch(OpStatus)
        {
        case CICS_MFMT_STATUS_EOF:
            MFMT_DEBUG(("return code indicates 'File empty'\n"));
            CICS_MFMT_ReleaseCatalogue();
            exit(0);

        case CICS_MFMT_STATUS_SYS_ERROR:
            MFMT_DEBUG(("return code is SYS_ERROR\n"));
            OutputMessage = CICS_MFMT_GetMessage(ERZ065003E,
                                                 CICS_MFMT_ERROR_SET);
            break;

        case CICS_MFMT_STATUS_FILE_EOF:
            MFMT_DEBUG(("return code is FILE_EOF\n"));
            OutputMessage = CICS_MFMT_GetMessage(ERZ065004E,
                                                 CICS_MFMT_ERROR_SET);
            break;

        case CICS_MFMT_STATUS_FILE_ERROR:
            MFMT_DEBUG(("return code is FILE_ERROR\n"));
            OutputMessage = CICS_MFMT_GetMessage(ERZ065005E,
                                                 CICS_MFMT_ERROR_SET);
            break;

        default:
            MFMT_DEBUG(("return code is UNKNOWN\n"));
            fprintf(stderr, "OpenMonitorFile: unknown return code.\n");
            fprintf(stderr, "Corruption of data\n");
            exit(1);
        }

        fprintf(stderr, OutputMessage, ArgV[1]);
        fprintf(stderr, "\n");
        CICS_MFMT_FreeMessage(OutputMessage);
        CICS_MFMT_ReleaseCatalogue();
        exit(2);
    }

    /*
     * Setup the header and footer buffers and output the initial header file.
     */
    FormatSetup(ArgV[1]);
    OutputPageHeader();

    /*
     * The first data record has been read by CICS_MFMT_OpenMonitorFile().
     * Use CICS_MFMT_FindField() to access each field that is stored in
     * the PrintItem structure. When all required items have been
     * accessed, get the next record. Repeat this procedure until all
     * the data has been accessed.
     */

    MFMT_DEBUG(("Beginning to output the stored data\n"));

    while (InOutStatus == CICS_MFMT_STATUS_OK)
    {
        for (Column = 0; Column < CICS_MFMT_DISPLAY_COLS; Column++)
        {
            MFMT_DEBUG(("Searching for field id %d\n", Fields[Column].FieldID));

            CurrentMonitorRec = CICS_MFMT_FindField(Fields[Column].FieldID,
                                                    DataStream);

            if (CurrentMonitorRec == (CICS_EMP_TDQ_RecordDetails_t *)NULL)
            {
                /*
                 * No data found for required field id. Something must
                 * be output or the data will not be aligned. Output a default
                 * format of whitespace.
                 */
                MFMT_DEBUG(("FieldId %d not found\n", Fields[Column].FieldID));

                fprintf(stdout, Fields[CICS_MFMT_DISPLAY_COLS].PrintFormat);
            }
            else
            {
                /*
                 * Depending on the particular type of the record, different
                 * fields of the union will be accessed to print out the
                 * value stored there.
                 */

                switch(CurrentMonitorRec->Type)
                {
                case CICS_EMP_TDQ_DETAIL_TYPE_COUNTER:
                    MFMT_DEBUG(("Record type is COUNTER\n"));

                    fprintf(stdout, Fields[Column].PrintFormat,
                                CurrentMonitorRec->Value.Counter);
                    break;

                case CICS_EMP_TDQ_DETAIL_TYPE_TIME_STAMP:
                    MFMT_DEBUG(("Record type is TIME_STAMP\n"));

                    /*
                     * This time stamp is composed of 2 longs, the first
                     * holding the number of seconds and the second holding
                     * the number of microseconds (1 million in a sec).
                     */

                    Time = CurrentMonitorRec->Value.TimeStamp.tv_sec;
                    Time += CurrentMonitorRec->Value.TimeStamp.tv_usec/1000000;

                    CurrentMonitorRec->Value.TimeStamp.tv_usec %= 1000000;

                    /*
                     * we now have the time in seconds (ANSI) and the remaining
                     * number of microseconds.
                     */

                    memcpy(&TimeData, localtime(&Time), sizeof(struct tm));

                    fprintf(stdout, Fields[Column].PrintFormat,
                        TimeData.tm_hour, TimeData.tm_min, TimeData.tm_sec,
                        CurrentMonitorRec->Value.TimeStamp.tv_usec/100);
                    break;

                case CICS_EMP_TDQ_DETAIL_TYPE_TIMER:
                    MFMT_DEBUG(("Record type is TIMER\n"));

                    /*
                     * The timer supplies the time slot in microseconds.
                     * We will convert this to seconds for output, as well as
                     * printing the remaining microseconds. Also the number of
                     * of times the clock was started/stopped.
                     */

                    Time = CurrentMonitorRec->Value.Timer.TotalTime / 1000000;
                    CurrentMonitorRec->Value.Timer.TotalTime %= 1000000;

                    fprintf(stdout, Fields[Column].PrintFormat,
                                Time, CurrentMonitorRec->Value.Timer.TotalTime,
                                CurrentMonitorRec->Value.Timer.Counter);
                    break;

                case CICS_EMP_TDQ_DETAIL_TYPE_OCCUPANCY:
                    MFMT_DEBUG(("Record type is OCCUPANCY\n"));

                    /*
                     * The occupancy value is stored as a 64 bit quantity.
                     * bit integer). Since we do not have any built-in printf
                     * functionality (ie at least on 32-bit operating systems),
                     * we have to manipulate the 64-bit data manually.
                     * Consequently, if the quantity is larger than can be
                     * expressed within 32 bits, we will scale the number
                     * before output.
                     * In order to do this we will, depending on the magnitude
                     * of the number, scale the number down by a
                     * factor of either 1024, 1024*1024, 1024*1024*1024, or
                     * 1024*1024*1024*1024. A suffix of either K (Kilo-),
                     * M (Mega-), G (Giga-) or T (Tera-) will then be used to
                     * denote the extent of scaling.
                     */

                    if (CurrentMonitorRec->Value.Occupancy.MSL == 0)
                    {
                       BitShift = 0;
                       strcpy(AppendChar,"");
                    }

                    else if (CurrentMonitorRec->Value.Occupancy.MSL > Zero &&
                                CurrentMonitorRec->Value.Occupancy.MSL < OneK)
                    {
                       BitShift = 10;
                       strcpy(AppendChar,"K");
                    }

                    else if (CurrentMonitorRec->Value.Occupancy.MSL >= OneK &&
                                 CurrentMonitorRec->Value.Occupancy.MSL < OneM)
                    {
                       BitShift = 20;
                       strcpy(AppendChar,"M");
                    }

                    else if (CurrentMonitorRec->Value.Occupancy.MSL >= OneM &&
                                 CurrentMonitorRec->Value.Occupancy.MSL < OneG)
                    {
                       BitShift = 30;
                       strcpy(AppendChar,"G");
                    }

                    else if (CurrentMonitorRec->Value.Occupancy.MSL >= OneG)
                    {
                       BitShift = 40;
                       strcpy(AppendChar,"T");
                    }

                    for (i=0; i < BitShift; i++)
                    {
                       CurrentMonitorRec->Value.Occupancy.LSL =
                                CurrentMonitorRec->Value.Occupancy.LSL >> 1;

                       if (CurrentMonitorRec->Value.Occupancy.MSL & MSWMask)
                       {
                           CurrentMonitorRec->Value.Occupancy.LSL =
                               CurrentMonitorRec->Value.Occupancy.LSL | LSWMask;
                       }

                       CurrentMonitorRec->Value.Occupancy.MSL =
                               CurrentMonitorRec->Value.Occupancy.MSL >> 1;

                    }

                    fprintf(stdout, Fields[Column].PrintFormat,
                                CurrentMonitorRec->Value.Occupancy.LSL,
                                AppendChar);
                    break;

                case CICS_EMP_TDQ_DETAIL_TYPE_STRING:
                    MFMT_DEBUG(("Record type is STRING\n"));
                    /*-------------------------------------------------*/
                    /* We cannot assume that character string data is  */
                    /* null-terminated, so use the length field from   */
                    /* the sub-record data structure to copy the       */
                    /* correct number of bytes into an intermediate    */
                    /* buffer.                                         */
                    /*-------------------------------------------------*/
                    for ( i=0; i < sizeof(StringBuffer); i++)
                        StringBuffer[i] = '\0';

                    strncpy(StringBuffer,CurrentMonitorRec->Value.String,
                                        CurrentMonitorRec->FieldSize);

                    fprintf(stdout, Fields[Column].PrintFormat,
                                            StringBuffer);


                    break;

                case CICS_EMP_TDQ_DETAIL_TYPE_ABEND_CODE:
                    MFMT_DEBUG(("Record type is ABEND_CODE\n"));
                    /*-------------------------------------------------*/
                    /* We cannot assume that character string data is  */
                    /* null-terminated, so use the length field from   */
                    /* the sub-record data structure to copy the       */
                    /* correct number of bytes into an intermediate    */
                    /* buffer.                                         */
                    /*-------------------------------------------------*/
                    for ( i=0; i < sizeof(StringBuffer); i++)
                        StringBuffer[i] = '\0';

                    strncpy(StringBuffer,CurrentMonitorRec->Value.String,
                                        CurrentMonitorRec->FieldSize);

                    fprintf(stdout, Fields[Column].PrintFormat,
                                            StringBuffer);

                    break;

                case CICS_EMP_TDQ_DETAIL_TYPE_HIGH_WATER:
                    MFMT_DEBUG(("Record type is HIGH_WATER\n"));
                    fprintf(stdout, Fields[Column].PrintFormat,
                                CurrentMonitorRec->Value.Counter);
                    break;

                case CICS_EMP_TDQ_DETAIL_TYPE_OS_COUNT:
                    MFMT_DEBUG(("Record type is OS_COUNT\n"));
                    fprintf(stdout, Fields[Column].PrintFormat,
                                CurrentMonitorRec->Value.Counter);
                    break;


                case CICS_EMP_TDQ_DETAIL_TYPE_OS_CLOCK:
                    MFMT_DEBUG(("Record type is OS_CLOCK\n"));
                    /*
                     * This Clock value is composed of 2 longs, the first
                     * holding the number of seconds and the second holding
                     * the number of microseconds (1 million in a sec).
                     */

                    Time = CurrentMonitorRec->Value.TimeStamp.tv_sec;
                    Time += CurrentMonitorRec->Value.TimeStamp.tv_usec/1000000;

                    CurrentMonitorRec->Value.TimeStamp.tv_usec %= 1000000;

                    /*
                     * we now have the time in seconds (ANSI) and the remaining
                     * number of microseconds.
                     */

                    memcpy(&TimeData, localtime(&Time), sizeof(struct tm));

                    fprintf(stdout, Fields[Column].PrintFormat,
                        TimeData.tm_hour, TimeData.tm_min, TimeData.tm_sec,
                        CurrentMonitorRec->Value.TimeStamp.tv_usec/100);


                case CICS_EMP_TDQ_DETAIL_TYPE_USER:
                    MFMT_DEBUG(("Record type is USER\n"));
					/*------------------------------------------------------------------------*/
					/* This is the case statement that is changed to print out the results    */
					/* from the user exit program and external monitoring points.  This case  */
					/* statement should be modified to print out the results desired form the */
					/* ULM.                                                                   */
					/*                                                                        */
					/* The current set up prints out the Monitoring Points and information    */
					/* only if they were used by the CICS program being monitored.  For       */
					/* normal transactions, the external monitoring points are not used       */
					/* and no information is printed out.                                     */
					/*------------------------------------------------------------------------*/

					header = 0;   /* reset header flag */

					/*
					 * The 'abt_emp' user exit program can have a max of 20 Entry Names
					 * This is why the loop executes a maximum of twenty times
					 */
					for (j = 0; j < 20; j++)
					{
					/*
					 * This checks to see if any clocks were used for this entry name,
					 * if they were, the appropriate information will be printed out
					 */

						if (CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clock1Used ||
							CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clock2Used ||
							CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clock3Used ||
							CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clock4Used ||
							CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clock5Used ||
							CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter1Used ||
							CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter2Used ||
							CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter3Used ||
							CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter4Used ||
							CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter5Used)
						{
				/*  Print out a header   */
							if (!header)
							{
								NewLine();
								fprintf(stdout, PageHeader[3]);
								header = 1;
							}

				/*  Print out the EntryName   */
							for (i=0; i< sizeof(StringBuffer); i++)
								StringBuffer[i] = '\0';
							strncpy(StringBuffer,
								CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].EntryName, 8);
							fprintf(stdout, "%s  ", StringBuffer);

				/*  Print out the Time for Clock One if used  */
							if (CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clock1Used)
							{
							    Time = CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time1
									/ 1000000;
					            CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time1 %= 1000000;

								fprintf(stdout, "%2d.%06d/%-5d  ", Time,
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time1,
								    CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Counter1);
							}
				/* Else Print out a filler */
							else
								fprintf(stdout, "      ---        ");

				/*  Print out the Time for Clock Two if used  */
							if (CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clock2Used)
							{
								Time2 = CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time2
									/ 1000000;
							    CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time2 %= 1000000;

								fprintf(stdout, "%2d.%06d/%-5d  ", Time2,
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time2,
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Counter2);
							}
				/* Else Print out a filler */
							else
								fprintf(stdout, "      ---        ");

				/*  Print out the Time for Clock Three if used  */
							if (CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clock3Used)
							{
								Time3 = CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time3
									/ 1000000;
								CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time3 %= 1000000;

								fprintf(stdout, "%2d.%06d/%-5d  ", Time3,
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time3,
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Counter3);
							}
				/* Else Print out a filler */
							else
								fprintf(stdout, "      ---        ");

				/*  Print out the Time for Clock Four if used  */
							if (CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clock4Used)
							{
								Time4 = CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time4
									/ 1000000;
					            CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time4 %= 1000000;

								fprintf(stdout, "%2d.%06d/%-5d  ", Time4,
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time4,
								    CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Counter4);
							}
				/* Else Print out a filler */
							else
								fprintf(stdout, "      ---        ");

				/*  Print out the Time for Clock Five if used  */
							if (CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clock5Used)
							{
								Time5 = CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time5
									/ 1000000;
								CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time5 %= 1000000;

								fprintf(stdout, "%2d.%06d/%-5d  ", Time5,
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Time5,
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Clk_Counter5);
							}
				/* Else Print out a filler */
							else
								fprintf(stdout, "      ---        ");

				/*  Print out the Time for Counter One if used  */
							if (CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter1Used)
							{
								fprintf(stdout, "%5i ",
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter1r);
							}
				/* Else Print out a filler */
							else
								fprintf(stdout, "  -   ");

				/*  Print out the Time for Counter Two if used  */
							if (CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter2Used)
							{
								fprintf(stdout, "%5i ",
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter2r);
							}
				/* Else Print out a filler */
							else
								fprintf(stdout, "  -   ");

				/*  Print out the Time for Counter Three if used  */
							if (CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter3Used)
							{
								fprintf(stdout, "%5i ",
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter3r);
							}
				/* Else Print out a filler */
							else
								fprintf(stdout, "  -   ");

				/*  Print out the Time for Counter Four if used  */
							if (CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter4Used)
							{
								fprintf(stdout, "%5i ",
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter4r);
							}
				/* Else Print out a filler */
							else
								fprintf(stdout, "  -   ");

				/*  Print out the Time for Counter Five if used  */
							if (CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter5Used)
							{
								fprintf(stdout, "%5i ",
									CurrentMonitorRec->Value.UserRecord.RecordEMPs[j].Counter5r);
							}
				/* Else Print out a filler */
							else
								fprintf(stdout, "  -   ");
							
							NewLine();
						}
					}

					break;

                default:
                    MFMT_DEBUG(("Record type is UNKNOWN\n"));
                    fprintf(stderr, "Unknown record type %d\n",
                                                CurrentMonitorRec->Type);
                    exit(4);
                } /* switch */
            }
        } /* for */

        /*
         * One complete row has been written out (or if the data
         * was not found, padding was substituted). Need to begin
         * next row of data on a new line.
         */

        NewLine();

        /*
         * get the next data record from the input file.
         */
        MFMT_DEBUG(("Read the next data record\n"));

        InOutStatus = CICS_MFMT_ReadNext(DataStream);

    } /* while */

    /*
     * When the data is exhausted (EOF or error), Output the footer
     * information for the last page. Need a blank line between data
     * and footer.
     */

    NewLine();
    OutputPageFooter();
    NewPage();

    /*
     * Report any errors obtained when reading the input data.
     */

    if ((InOutStatus != CICS_MFMT_STATUS_OK) &&
        (InOutStatus != CICS_MFMT_STATUS_EOF))
    {
        switch (InOutStatus)
        {
        case CICS_MFMT_STATUS_SYS_ERROR:
            MFMT_DEBUG(("return code is SYS_ERROR\n"));
            OutputMessage = CICS_MFMT_GetMessage(ERZ065003E,
                                                 CICS_MFMT_ERROR_SET);
            break;

        case CICS_MFMT_STATUS_FILE_EOF:
            MFMT_DEBUG(("return code is FILE_EOF\n"));
            OutputMessage = CICS_MFMT_GetMessage(ERZ065004E,
                                                 CICS_MFMT_ERROR_SET);
            break;

        case CICS_MFMT_STATUS_FILE_ERROR:
            MFMT_DEBUG(("return code is FILE_ERROR\n"));
            OutputMessage = CICS_MFMT_GetMessage(ERZ065005E,
                                                 CICS_MFMT_ERROR_SET);
            break;

        default:
            MFMT_DEBUG(("return code is UNKNOWN\n"));
            fprintf(stderr, "OpenMonitorFile: unknown return code.\n");
            fprintf(stderr, "Corruption of data\n");
            exit(1);
        }

        if (OutputMessage != NULL)
        {
            fprintf(stderr, OutputMessage, ArgV[1]);
            fprintf(stderr, "\n");
            CICS_MFMT_FreeMessage(OutputMessage);
        }

        if (CICS_MFMT_CloseMonitorFile(DataStream) != 0)
        {
            perror("Error Closing monitor file");
        }
        CICS_MFMT_ReleaseCatalogue();
        exit(2);
    }

    /*
     * Close the input file and the message catalogue.
     */
    if (CICS_MFMT_CloseMonitorFile(DataStream) != 0)
    {
        perror("Error Closing monitor file");
    }

    CICS_MFMT_ReleaseCatalogue();
    exit(0);
}

/*
 * FUNCTION:    FormatSetup();
 *
 * This routine obtains static information ie. Date, time of last
 * data modification of the file, etc. Using this and other information
 * the page headers are formed as well as a section of the page footer
 * (the footer is not constant as it contains the page number). The
 * single parameter is a pointer to the name of the input file.
 */

/* The following constants define the maximum field widths for the
 * translated catalogue messages. If the translated message does not fit
 * into the width defined, it will be automatically truncated so as to not
 * overwrite the end of the arrays used to hold the headers and footer.
 */
#define HEADER_TITLE_SPACE 90   /* maximum length of header title msg */
#define HEADER_DATE_SPACE  42   /* maximum length of header date msg  */
#define FOOTER_TITLE_SPACE 46   /* maximum length of footer title msg */
#define FOOTER_DATE_SPACE  76   /* maximum length of footer date msg  */
#define FOOTER_PAGE_SPACE  7    /* maximum length of footer page msg  */


void FormatSetup(cics_char_t *InputFile)
{
    cics_char_t WorkBuffer[CICS_MFMT_PAGE_WIDTH+1];   /* space to manipulate */
                                                      /* strings             */
    struct tm TimeData;                        /* for manipulating date/time */
    time_t Time;                               /* hold the current time */
    int Line, Column;                          /* current (x,y) position */
    cics_char_t *BufferPtr;                    /* ptr to current write pos */
    cics_char_t *OutputMessage;                /* ptr to current output msg */
    cics_char_t *DateMessage;                  /* ptr to date output message */
    struct stat FileDetails;                   /* last modification of file */
    int WorkBuffLen;                           /* size of data buffer */
    cics_char_t *BaseFile;                     /* File name, less path info */
    int WriteError;                            /* end of buffer overwritten */

    WriteError = FALSE;

    /*
     * First we `zero' all the static buffers that we use. This involves
     * initialising them to white space. Then build up the header as it
     * will appear on the page. All information in the header is constant.
     */

    for (Line = 0; Line < CICS_MFMT_MAX_HEADERS; Line++)
    {
        memset(PageHeader[Line], ' ', CICS_MFMT_PAGE_WIDTH);
        PageHeader[Line][CICS_MFMT_PAGE_WIDTH+1] = '\0';  /* terminate buffer */
    }
    memset(PageFooter, ' ', CICS_MFMT_PAGE_WIDTH);
    PageFooter[CICS_MFMT_PAGE_WIDTH + 1] = '\0';          /* terminate buffer */


    BufferPtr = PageHeader[0];

    MFMT_DEBUG(("Getting title of report\n"));

    if ((OutputMessage = CICS_MFMT_GetMessage(CICS_MFMT_SRH_NAME,
                                         CICS_MFMT_REPORT_SET)) == NULL)
    {
        perror("Failed to access message catalogue[1]");
    }
    else
    {

        MFMT_DEBUG(("Get the current date for report header\n"));

        Time = time(NULL);
        memcpy(&TimeData, localtime(&Time), sizeof(struct tm));
        if ((DateMessage = CICS_MFMT_GetMessage(CICS_MFMT_SRH_DATE,
                                                CICS_MFMT_REPORT_SET)) == NULL)
        {
            perror("Failed to access message catalogue[2]");
        }
        else
        {
            /*
             * Copy the Date message, and date to the work buffer.
             */
            if ((sprintf(WorkBuffer, DateMessage,
                         TimeData.tm_mday, TimeData.tm_mon + 1,
                         TimeData.tm_year % 100)) > CICS_MFMT_PAGE_WIDTH)
            {
                WriteError = TRUE;
            }
            else
            {
                /*
                 * Copy the Title message and work buffer to the header array.
                 */
                if ((sprintf(BufferPtr, "%-*s%*s", HEADER_TITLE_SPACE,
                             OutputMessage, HEADER_DATE_SPACE, WorkBuffer))
                             > CICS_MFMT_PAGE_WIDTH)
                {
                    WriteError = TRUE;
                }
            }
            CICS_MFMT_FreeMessage(OutputMessage);
            CICS_MFMT_FreeMessage(DateMessage);

            if (WriteError)
            {
                fprintf(stderr,"Message too long for buffer\n");
                exit(1);
            }
        }
    }

    /*
     * Build the Title for the report.
     */
    MFMT_DEBUG(("Format the data titles\n"));

    for (Line = 0; Line < CICS_MFMT_TITLE_LINES; Line++)
    {
        /*
         * for each column, obtain the title for that column and
         * store it in the header buffer.
         */

        BufferPtr = PageHeader[Line + 1];

        for (Column = 0; Column < CICS_MFMT_DISPLAY_COLS; Column++)
        {
            /*
             * Get the text for the header of this column. If NULL
             * is returned then there was an error accessing the
             * catalogue. Therefore, use the default text for the
             * header.
             */
            MFMT_DEBUG(("Column number is %d\n", Column));

            /*
             * Need to get the text for a column header only if
             * there is a column header.
             */

            if (TitleLine[Line][Column].MessageNumber == CICS_MFMT_NO_TEXT)
            {
                OutputMessage = NULL;  /* no text needed for this column */
            }
            else
            {
                MFMT_DEBUG(("Looking for message %d in set %d\n",
                  TitleLine[Line][Column].MessageNumber, CICS_MFMT_REPORT_SET));

                OutputMessage = CICS_MFMT_GetMessage(
                                        TitleLine[Line][Column].MessageNumber,
                                        CICS_MFMT_REPORT_SET);

                if (OutputMessage == NULL)
                {
                    MFMT_DEBUG(("Use default header - error in catalog\n"));
                    OutputMessage = TitleLine[Line][Column].DefaultText;
                }
            }
            MFMT_DEBUG(("Build column title in temp buffer\n"));

            if ((sprintf(WorkBuffer, TitleLine[Line][Column].PrintFormat,
                         OutputMessage)) > CICS_MFMT_PAGE_WIDTH)
            {
                WriteError = TRUE;
            }


            MFMT_DEBUG(("Ptr to output message %#x\n", OutputMessage));

            /*
             * Free memory used by the header (if any).
             */
            if ((OutputMessage != NULL) && (*OutputMessage != '\0'))
            {
                MFMT_DEBUG(("Freeing memory used by the header text\n"));
                CICS_MFMT_FreeMessage(OutputMessage);
            }

            if (WriteError)
            {
                fprintf(stderr,"Message too long for buffer\n");
                exit(2);
            }
            /*
             * Output the current section of the header that had just been
             * prepared.
             */
            MFMT_DEBUG(("Store header section [%s]\n", WorkBuffer));
            memcpy(BufferPtr, WorkBuffer, strlen(WorkBuffer));
            BufferPtr += strlen(WorkBuffer);

        } /* for Column */

        /*
         * Terminate the current buffer being written to.
         */
        *(BufferPtr++)   = '\n';
        *(BufferPtr) = '\0';

    } /* for Line */


    /*
     * Now with the headers setup, begin to construct the footer.
     * The footer is organised in a different manner as its contents
     * are variable. The page number must be appended at each use.
     * This problem is overcome by passing the entire footer as a
     * format statement to fprintf(), supplying the page number as
     * the only parameter.
     */

    BufferPtr = PageFooter;

    BaseFile = (cics_char_t *) strrchr(InputFile, DIR_SEPARATOR_CHAR);
    BaseFile = ((BaseFile == NULL) ? InputFile : BaseFile + 1);

    if ((OutputMessage = CICS_MFMT_GetMessage(CICS_MFMT_SRF_FILE,
                                              CICS_MFMT_REPORT_SET)) == NULL)
    {
        perror("Failed to access message catalogue[3]");
    }
    else
    {
        /*
         * Write the Monitoring File title and file name to the work buffer
         * and free the message buffer.
         */
        if ((sprintf(WorkBuffer,"%-s%-s", OutputMessage, BaseFile))
                     > CICS_MFMT_PAGE_WIDTH)
        {
            WriteError = TRUE;
        }

        CICS_MFMT_FreeMessage(OutputMessage);

        if (WriteError)
        {
            fprintf(stderr,"Message too long for buffer\n");
            exit(3);
        }

        /*
         * Write work buffer to the footer array, within the max field width.
         */
        BufferPtr += sprintf(BufferPtr, "%-*s", FOOTER_TITLE_SPACE, WorkBuffer);

        if ((BufferPtr - PageFooter) > CICS_MFMT_PAGE_WIDTH)
        {
            fprintf(stderr,"Message too long for buffer\n");
            exit(4);
        }

    }

    /*
     * Obtain details of last data modification on the data file.
     * Process this information only if no error has occurred
     * on access.
     */

    if (stat(InputFile, &FileDetails) == 0)
    {
        /*
         * get the time of last data modification for the data file
         */
        memcpy(&TimeData, localtime(&FileDetails.st_mtime), sizeof(struct tm));
        if (TimeData.tm_isdst > 0)
        {
            /*
             * Daylight Saving Time is in effect. Users time is one
             * hour ahead of that supplied.
             */
            TimeData.tm_hour++;
        }
        if ((OutputMessage = CICS_MFMT_GetMessage(CICS_MFMT_SRF_DATE,
                                      CICS_MFMT_REPORT_SET)) == NULL)
        {
            perror("Failed to access message catalogue[4]");
        }
        else
        {
            if ((sprintf(WorkBuffer, OutputMessage, TimeData.tm_mday,
                         TimeData.tm_mon+1, TimeData.tm_year % 100,
                         TimeData.tm_hour, TimeData.tm_min,
                         TimeData.tm_sec)) > CICS_MFMT_PAGE_WIDTH)
            {
                WriteError = TRUE;
            }

            CICS_MFMT_FreeMessage(OutputMessage);

            if (WriteError)
            {
                fprintf(stderr,"Message too long for buffer\n");
                exit(5);
            }

            /*
             * Write work buffer to the footer array, within max field width.
             */
            BufferPtr += sprintf(BufferPtr, "%-*s",
                                 FOOTER_DATE_SPACE, WorkBuffer);

            if ((BufferPtr - PageFooter) > CICS_MFMT_PAGE_WIDTH)
            {
                fprintf(stderr,"Message too long for buffer\n");
                exit(6);
            }
        }
    }

    /*
     * The page number is variable, so include in the footer a format
     * descriptor that is recognisable by the *printf() routines.
     */

    if ((OutputMessage = CICS_MFMT_GetMessage(CICS_MFMT_SRF_PAGE,
                                         CICS_MFMT_REPORT_SET)) == NULL)
    {
        perror("Failed to access message catalogue[5]");
    }
    else
    {
        /*
         * Write the Page title to the footer array, within max field width.
         */
        BufferPtr += sprintf(BufferPtr, "%*s",
                             FOOTER_PAGE_SPACE, OutputMessage);
        CICS_MFMT_FreeMessage(OutputMessage);

        if ((BufferPtr - PageFooter) > CICS_MFMT_PAGE_WIDTH)
        {
            fprintf(stderr,"Message too long for buffer\n");
            exit(7);
        }

        memcpy(BufferPtr, "%.3d", 5);
    }

    /*
     * By copying one extra character from the final format string (size 4),
     * we are also copying the NULL character which will terminate the
     * footer.
     */
}


/*
 * FUNCTION:    OutputPageHeader()
 *
 * This routine outputs the preformatted page headers. It also sets
 * up the output for printing of data (ie. supplies necessary newline
 * chars).
 */

void OutputPageHeader(void)
{
    fprintf(stdout, PageHeader[0]);
    fprintf(stdout, "\n\n");            /* blank line after page header */
    fprintf(stdout, PageHeader[1]);
    fprintf(stdout, PageHeader[2]);

    fprintf(stdout, "\n");              /* blank line before data is written */
    LineNo = 6;
}


/*
 * FUNCTION:    OutputPageFooter()
 *
 * This routine outputs the preformatted page footer. The current page number
 * is supplied. Only the footer line is output, other formatting
 * requirements (ie formfeed char) must be output here.
 */

void OutputPageFooter(void)
{
    fprintf(stdout, "\n");
    fprintf(stdout, PageFooter, PageNo);
}


/*
 * FUNCTION:    NewPage()
 *
 * Perform whatever operation is required to begin output on a new page.
 */

void NewPage(void)
{
    fprintf(stdout, "\f\n");
    LineNo = 1;
    PageNo++;
}

void NewLine()
{
    fprintf(stdout, "\n");
    LineNo++;
    if (LineNo > CICS_MFMT_PAGE_SIZE - 2)
    {
        OutputPageFooter();
        NewPage();
        OutputPageHeader();
    }

}
