/********************************************************************/
/*                                                                  */
/*   MQREPLY has the following parameters.                          */
/*                                                                  */
/*      -f name of the parameters file                              */
/*      -r name of the reply data file                              */
/*      -t sleep time in milliseconds (wait before replying)        */
/*      -m override the queue manager name in the parameters file.  */
/*      -q override the queue  name in the parameters file.         */
/*      -l issue mqget with logical order specified (MQ groups)     */
/*      -s silent mode; no normal operational messages.             */
/*                                                                  */
/*    if no queue manager is specified, the default queue manager   */
/*    is used.                                                      */
/*                                                                  */
/********************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#include <windows.h>
#include "signal.h"
#endif

#ifdef SOLARIS
#include <ctype.h>
#include <unistd.h>
#include <sys/time.h>
#endif

/* includes for MQI */
#include <cmqc.h>

/* common subroutines include */
#include "int64defs.h"
#include "comsubs.h"

/* MQ subroutines include */
#include "qsubs.h"

#ifndef WIN32
void Sleep(int amount)
{
	usleep(amount*1000);
}
#endif

#define MAX_MESSAGE_LENGTH	256 * 65536 + 1

#define QNAME				"QNAME"
#define QMGR				"QMGR"
#define CCSID				"CCSID"
#define ENCODING			"ENCODING"
#define FORMAT				"FORMAT"
#define MAXMSGLEN			"MAXMSGLEN"
#define MAXWAITTIME			"MAXWAITTIME"
#define SLEEPTIME			"SLEEPTIME"
#define RESENDRFHMCD		"RESENDRFHMCD"
#define RESENDRFHJMS		"RESENDRFHJMS"
#define RESENDRFHPSC		"RESENDRFHPSC"
#define RESENDRFHPSCR		"RESENDRFHPSCR"
#define RESENDRFHUSR		"RESENDRFHUSR"
#define RESENDRFH			"RESENDRFH"

/**************************************************************/
/*                                                            */
/* Parameter values from parameter file.                      */
/*                                                            */
/**************************************************************/

typedef struct {
	/* global error switch */
	int		err;

	/* handles for the reply queue */
	MQLONG	replyQ;

	/* silent mode indicator - do not issue normal operational messages */
	int silent;

	/* names of the last used queue manager and queue */
	char	replyQMname[MQ_Q_MGR_NAME_LENGTH + 1];
	char	replyQname[MQ_Q_NAME_LENGTH + 1];

	/* Universal parameters */
	int		ccsid;
	int		encoding;
	int		logicalOrder;
	int		resendRFHusr;
	int		resendRFHjms;
	int		resendRFHmcd;
	int		resendRFHpsc;
	int		resendRFHpscr;
	int		resendRFH;
	int		sleeptime;
	int		saveSleeptime;
	int		maxMsgLen;
	int		maxWaitTime;

	char	qname[MQ_Q_NAME_LENGTH + 1];
	char	saveQname[MQ_Q_NAME_LENGTH + 1];
	char	msgFormat[MQ_FORMAT_LENGTH + 1];
#ifdef MQCLIENT
	char	qmname[512];
	char	saveQMname[512];
#else
	char	qmname[MQ_Q_MGR_NAME_LENGTH + 1];
	char	saveQMname[MQ_Q_MGR_NAME_LENGTH + 1];
#endif

	/* name of the parameters file */
	char	parmFilename[512];
	char	replyFilename[512];

	/* reply count */
	int replyCount;
} replyParms;

	/* global termination switch */
	volatile int	terminate=0;

static char copyright[] = "(C) Copyright IBM Corp, 2001/2002/2005\n";
static char Version[]=\
"@(#)MQReply V2.0 - Message Broker MQ reply program  - Jim MacNair ";

#ifdef _DEBUG
static char Level[]="mqreply.c V2.0 Debug version ("__DATE__" "__TIME__")";
#else
static char Level[]="mqreply.c V2.0 Release version ("__DATE__" "__TIME__")";
#endif

void captureString(char * value, char * arg, const int maxlen, replyParms * parms)

{
	if ((int) strlen(arg) < maxlen)
	{
		strcpy(value, arg);
	}
	else
	{
		/* issue an error message */
		printf("*****Value of constant exceeds maximum length\n");

		/* Set the return code based on the message number */
		parms->err = 94;
	}
}

int getArgPointer(int argc,
				  char *argv[],
				  int index,
				  char ** parmData,
				  const char * parmDesc,
				  replyParms * parms)

{
	if (strlen(argv[index]) > 2)
	{
		/* point to the argument data */
		(*parmData) = argv[index] + 2;
	}
	else
	{
		index++;

		if (index < argc)
		{
			/* point to the argument data */
			(*parmData) = argv[index];
		}
		else
		{
			/* issue an error message */
			printf("*****%s parameter is missing value\n", parmDesc);
			
			/* Set the return code to indicate the error */
			parms->err = 95;

			/*point to something valid */
			(*parmData) = argv[index - 1] + 2;
		}
	}


	return index;
}

int processIndArg(int argc, 
				  char *argv[], 
				  int index, 
				  char * parm, 
				  int parmsize, 
				  const char * parmDesc,
				  replyParms * parms)

{
	char *argData;

	index = getArgPointer(argc, argv, index, &argData, parmDesc, parms);

	if (index < argc)
	{
		/* value is included with parameter */
		captureString(parm, argData, parmsize, parms);
	}

	return index;
}

void processArgs(int argc, char **argv, replyParms * parms)

{
	int		i;
	int		foundit;
	char	ch;
	char	logOrd[32];
	char	stime[32];

	i = 1;
	while ((i < argc) && (0 == parms->err))
	{
		foundit = 0;

		if ('-' == argv[i][0])
		{
			ch = toupper(argv[i][1]);

			/* check for option */
			if ((0 == foundit) && ('Q' == ch))
			{
				foundit = 1;

				i = processIndArg(argc, 
								  argv, 
								  i,
								  parms->saveQname, 
								  sizeof(parms->saveQname), 
								  "queue name (-q)",
								  parms);
			}

			/* check for option */
			if ((0 == foundit) && ('M' == ch))
			{
				foundit = 1;

				i = processIndArg(argc, 
								  argv, 
								  i,
								  parms->saveQMname, 
								  sizeof(parms->saveQMname), 
								  "queue manager name (-m)",
								  parms);
			}

			/* check for option */
			if ((0 == foundit) && ('F' == ch))
			{
				foundit = 1;

				i = processIndArg(argc, 
								  argv, 
								  i,
								  parms->parmFilename, 
								  sizeof(parms->parmFilename), 
								  "name of parameters file (-f)",
								  parms);
			}

			/* check for option */
			if ((0 == foundit) && ('R' == ch))
			{
				foundit = 1;

				i = processIndArg(argc, 
								  argv, 
								  i,
								  parms->replyFilename, 
								  sizeof(parms->replyFilename), 
								  "name of reply data file (-o)",
								  parms);
			}

			/* check for silent option */
			if ((0 == foundit) && ('S' == ch))
			{
				foundit = 1;
				parms->silent = 1;
			}

			/* check for logical order option */
			if ((0 == foundit) && ('L' == ch))
			{
				foundit = 1;

				i = processIndArg(argc, 
								  argv, 
								  i,
								  logOrd, 
								  sizeof(logOrd), 
								  "use logical order (-l)",
								  parms);

				if (logOrd[1] == '1')
				{
					parms->logicalOrder = 1;
				}
			}

			/* check for sleep time option */
			if ((0 == foundit) && ('T' == ch))
			{
				foundit = 1;

				i = processIndArg(argc, 
								  argv, 
								  i,
								  stime, 
								  sizeof(stime), 
								  "sleeptime (-t)",
								  parms);

				parms->saveSleeptime = atoi(stime);
				if (parms->saveSleeptime < -1)
				{
					parms->saveSleeptime = -1;
				}
			}
		}

		/* did we recognize the parameter? */
		if (0 == foundit)
		{
			/* issue an error message */
			printf("*****Unrecognized command line parameter found %c\n", argv[i][0]);

			/* Set the return code based on the message number */
			parms->err = 93;
		}

		i++;
	}
}

/**************************************************************/
/*                                                            */
/* Process a line in the parameters file.                     */
/*                                                            */
/**************************************************************/

int evaluateParm(char * ptr, char * value, replyParms * parms)

{
	int foundit=0;
	int len;
	char *valueptr;

	/* remove any quotes from the value */
	valueptr = removeQuotes(value);
	len = strlen(valueptr);

	if (strcmp(ptr, QNAME) == 0)
	{
		foundit = 1;
		if (len > MQ_Q_NAME_LENGTH)
		{
			/* parameter too long, issue warning and truncate */
			printf("***** Queue name - parameter too long (%d) - data truncated - (%s)\n", 
					len, value);

			len = MQ_Q_NAME_LENGTH;
		}

		/* check for previous setting */
		if (parms->qname[0] > ' ')
		{
			/* issue warning message */
			printf("***** output queue name has been specified previously, new value (%d) used\n",
					valueptr);
		}

		/* get the output queue name */
		memset(parms->qname, 0, sizeof(parms->qname));
		memcpy(parms->qname, valueptr, len);
	}

	if (strcmp(ptr, QMGR) == 0)
	{
		foundit = 1;
		if (len > sizeof(parms->qmname) - 1)
		{
			/* parameter too long, issue warning and truncate */
			printf("***** Queue manager name - parameter too long (%d) - data truncated - (%s)\n", 
					len, value);

			len = sizeof(parms->qmname) - 1;
		}

		/* check for previous setting */
		if (parms->qmname[0] > ' ')
		{
			/* issue warning message */
			printf("***** output queue manager has been specified previously, new value (%d) used\n",
					valueptr);
		}

		/* get the output queue manager name to connect to */
		memset(parms->qmname, 0, sizeof(parms->qmname));
		memcpy(parms->qmname, valueptr, len);
	}

	if (strcmp(ptr, FORMAT) == 0)
	{
		foundit = 1;
		if (len > sizeof(parms->msgFormat) - 1)
		{
			/* parameter too long, issue warning and truncate */
			printf("***** MQ format - parameter too long (%d) - data truncated - (%s)\n", 
					len, value);

			len = sizeof(parms->msgFormat) - 1;
		}

		/* check for previous setting */
		if (parms->msgFormat[0] > ' ')
		{
			/* issue warning message */
			printf("***** output message format has been specified previously, new value (%d) used\n",
					valueptr);
		}

		/* get the output queue manager name to connect to */
		memset(parms->msgFormat, 0, sizeof(parms->msgFormat));
		memcpy(parms->msgFormat, valueptr, len);
	}

	if (strcmp(ptr, CCSID) == 0)
	{
		foundit = 1;

		/* check for previous setting */
		if (parms->ccsid != 0)
		{
			/* issue warning message */
			printf("***** CCSID has been specified previously, new value (%d) used\n",
					parms->ccsid);
		}

		/* set the ccsid value */
		parms->ccsid = atoi(valueptr);
	}

	if (strcmp(ptr, ENCODING) == 0)
	{
		foundit = 1;

		/* check for previous setting */
		if (parms->encoding != 0)
		{
			/* issue warning message */
			printf("***** ENCODING has been specified previously, new value (%d) used\n",
					parms->encoding);
		}

		/* set the ccsid value */
		parms->encoding = atoi(valueptr);
	}

	if (strcmp(ptr, MAXWAITTIME) == 0)
	{
		foundit = 1;

		parms->maxWaitTime = atoi(valueptr);
		if (parms->maxWaitTime < 0)
		{
			printf("***** invalid value for MaxWaitTime %d *****\n", parms->maxWaitTime);
			parms->maxWaitTime = 120;
		}
	}

	if (strcmp(ptr, SLEEPTIME) == 0)
	{
		foundit = 1;

		parms->sleeptime = atoi(valueptr);
		if (parms->sleeptime < 0)
		{
			printf("***** invalid value for sleeptime %d *****\n", parms->sleeptime);
			parms->sleeptime = 0;
		}
	}

	if (strcmp(ptr, MAXMSGLEN) == 0)
	{
		foundit = 1;

		parms->maxMsgLen = atoi(valueptr) + 1;
		if (parms->maxMsgLen < 2)
		{
			printf("***** invalid value for MaxMsgLen %d *****\n", parms->maxMsgLen);
			parms->maxMsgLen = MAX_MESSAGE_LENGTH;
		}
	}

	if (strcmp(ptr, RESENDRFHMCD) == 0)
	{
		foundit = 1;

		if (('Y' == valueptr[0]) || ('y' == valueptr[0]) || ('1' == valueptr[0]))
		{
			parms->resendRFHmcd = 1;
			parms->resendRFH = 1;
		}
		else
		{
			parms->resendRFHmcd = 0;
		}
	}

	if (strcmp(ptr, RESENDRFHJMS) == 0)
	{
		foundit = 1;

		if (('Y' == valueptr[0]) || ('y' == valueptr[0]) || ('1' == valueptr[0]))
		{
			parms->resendRFHjms = 1;
			parms->resendRFH = 1;
		}
		else
		{
			parms->resendRFHjms = 0;
		}
	}

	if (strcmp(ptr, RESENDRFHPSC) == 0)
	{
		foundit = 1;

		if (('Y' == valueptr[0]) || ('y' == valueptr[0]) || ('1' == valueptr[0]))
		{
			parms->resendRFHpsc = 1;
			parms->resendRFH = 1;
		}
		else
		{
			parms->resendRFHpsc = 0;
		}
	}

	if (strcmp(ptr, RESENDRFHPSCR) == 0)
	{
		foundit = 1;

		if (('Y' == valueptr[0]) || ('y' == valueptr[0]) || ('1' == valueptr[0]))
		{
			parms->resendRFHpscr = 1;
			parms->resendRFH = 1;
		}
		else
		{
			parms->resendRFHpscr = 0;
		}
	}

	if (strcmp(ptr, RESENDRFHUSR) == 0)
	{
		foundit = 1;

		if (('Y' == valueptr[0]) || ('y' == valueptr[0]) || ('1' == valueptr[0]))
		{
			parms->resendRFHusr = 1;
			parms->resendRFH = 1;
		}
		else
		{
			parms->resendRFHusr = 0;
		}
	}

	if (strcmp(ptr, RESENDRFH) == 0)
	{
		foundit = 1;

		if (('Y' == valueptr[0]) || ('y' == valueptr[0]) || ('1' == valueptr[0]))
		{
			parms->resendRFH = 1;
		}
		else
		{
			parms->resendRFH = 0;
		}
	}

	return foundit;
}

/**************************************************************/
/*                                                            */
/* Find the parameter and the value.                          */
/*                                                            */
/**************************************************************/

void processParmLine(char * ptr, replyParms * parms)

{
	int		foundit=0;
	char *	valueptr;
	char *	blankptr;

	/* find the equal equal sign */
	valueptr = strchr(ptr, '=');

	/* did we find one? */
	if (valueptr != NULL)
	{
		/* break the line into name and value parts */
		valueptr[0] = 0;
		valueptr++;

		/* find the beginning of the value */
		valueptr = skipBlanks(valueptr);
	}

	/* strip trailing blanks and translate the name to upper case */
	blankptr = findBlank(ptr);
	blankptr[0] = 0;
	strupper(ptr);

	/* is this a name value pair? */
	if (valueptr != NULL)
	{
		/* check that we have a value */
		if (valueptr[0] > 0)
		{
			foundit = evaluateParm(ptr, valueptr, parms);

			if (foundit == 0)
			{
				printf("***** unrecognized parameter %s, value %s\n", ptr, valueptr);
			}
		}
		else
		{
			printf("***** missing or blank value for parameter %s\n", ptr);
		}
	}
}

void processParms(FILE *parmfile, replyParms * parms)

{
	int		len;
	char *	ptr;
	char	parmline[512];

	/* read the parameter file */
	while (fgets(parmline, sizeof(parmline) - 1, parmfile) != NULL)
	{
		/* check for a new line character at the end of the line */
		while ((strlen(parmline) > 0) && (parmline[strlen(parmline) - 1] < ' '))
		{
			/* get rid of the new line character */
			parmline[strlen(parmline) - 1] = 0;
		}

		/* point to the beginning of the line */
		/* skip any leading blanks */
		ptr = skipBlanks(parmline);

		/* truncate any trailing blanks */
		len = strlen(ptr);
		while ((len > 0) && (ptr[len] == ' '))
		{
			ptr[len] = 0;
			len--;
		}

		/* check for a comment or blank line */
		if ((ptr[0] != 0) && (ptr[0] != ';') && (ptr[0] != '#') && (ptr[0] != '*'))
		{
			/* process this line */
			processParmLine(ptr, parms);

			/* re-initialize the parameter input area */
			memset(parmline, 0, sizeof(parmline));
		}
	}
}

int processParmFile(replyParms * parms)

{
	int		rc=0;
	int		len=0;
	FILE	*parmfile;

	parmfile = fopen(parms->parmFilename, "r");

	if (NULL == parmfile)
	{
		rc = 1;
		printf("unable to open parameters file %s\n", parms->parmFilename);
	}
	else
	{
		/* process the parameters file */
		processParms(parmfile, parms);

		/* close the parameters file */
		fclose(parmfile);
	}

	return rc;
}

int checkRFH(const char * msgdata, const int datalen, replyParms * parms)

{
	MQRFH2	tempRFH;
	char	tempEbcdic[32];
	int		rfhlength=0;
	int		rfhversion;
	int		ebcdic=0;

	/* check if the message is long enough to have an RFH */
	if (datalen < sizeof(tempRFH))
	{
		return 0;
	}

	/* get a copy of the first 36 bytes of the message */
	memcpy(&tempRFH, msgdata, sizeof(tempRFH));

	if (memcmp(tempRFH.StrucId, MQRFH_STRUC_ID, sizeof(tempRFH.StrucId)) != 0)
	{
		/* try converting the StrucId field to EBCDIC */
		EbcdicToAscii((unsigned char *) &tempRFH.StrucId, sizeof(tempRFH.StrucId), (unsigned char *)tempEbcdic);
		memcpy(&tempRFH.StrucId, tempEbcdic, sizeof(tempRFH.StrucId));
		ebcdic = 1;

		/* see if we have a structure identifier in EBCDIC */
		if (memcmp(tempRFH.StrucId, MQRFH_STRUC_ID, sizeof(tempRFH.StrucId)) != 0)
		{
			/* no RFH found */
			return 0;
		}
	}

	/* see if we can recognize the version and length */
	rfhversion = tempRFH.Version;
	rfhlength = tempRFH.StrucLength;

	/* check if we have the right encoding */
	if ((MQRFH_VERSION_1 != rfhversion) && (MQRFH_VERSION_2 != rfhversion))
	{
		/* reverse the bytes and see if we recognize the version */
		rfhversion = reverseBytes4(rfhversion);
		rfhlength = reverseBytes4(rfhlength);

		if ((MQRFH_VERSION_1 != rfhversion) && (MQRFH_VERSION_2 != rfhversion))
		{
			return 0;
		}
	}

	/* finally check that the length makes sense */
	if ((rfhlength < MQRFH_STRUC_LENGTH_FIXED) || (rfhlength > datalen))
	{
		/* length doesn't make sense */
		return 0;
	}

	return rfhlength;
}

/**************************************************************************************/
/*                                                                                    */
/* perform special processing on the jms folder when using it as the basis of a reply */
/* The Dst element should be removed and the Rto element should be renamed to Dst     */
/*                                                                                    */
/**************************************************************************************/

int processJMSfolder(char * inptr, char * outptr, int segLen)

{
	int		foundDst=0;
	int		foundRto=0;
	int		bytesMoved=0;
	int		i=0;
	int		extra;

	/* remove any trailing blanks */
	while ((segLen > 0) && (' ' == inptr[segLen - 1]))
	{
		segLen--;
	}

	/* start copying the input to the output */
	while (segLen > 0)
	{
		if ((1 == foundDst) || (1 == foundRto))
		{
			if ('<' == inptr[0])
			{
				/* check if we have found the end of the element */
				if (1 == foundRto)
				{
					/* check for the ending element tag */
					if (memcmp(inptr, "</Rto>", 6) == 0)
					{
						memcpy(outptr, "</Dst>", 6);
						inptr += 6;
						outptr += 6;
						bytesMoved += 6;
						segLen -= 6;
						foundRto = 0;
					}
					else
					{
						/* this should not occur but try to be graceful */
						outptr++[0] = inptr++[0];
						segLen--;
						bytesMoved++;
					}
				}
				else
				{
					/* check for the ending element tag */
					if (memcmp(inptr, "</Dst>", 6) == 0)
					{
						inptr += 6;
						segLen -= 6;
						foundDst = 0;
					}
					else
					{
						/* this should not occur but try to be graceful */
						inptr++;
						segLen--;
					}
				}
			}
			else
			{
				/* handle the next character of the element value */
				if (1 == foundRto)
				{
					outptr++[0] = inptr++[0];
					bytesMoved++;
				}
				else
				{
					inptr++;
				}

				segLen--;
			}
		}
		else
		{
			if ('<' == inptr[0])
			{
				if (memcmp(inptr, "<Dst>", 5) == 0)
				{
					foundDst = 1;
					inptr += 5;
					segLen -= 5;
				}
				else
				{
					if (memcmp(inptr, "<Rto>", 5) == 0)
					{
						foundRto = 1;
						memcpy(outptr, "<Dst>", 5);
						inptr += 5;
						outptr += 5;
						bytesMoved += 5;
						segLen -= 5;
					}
					else
					{
						/* Not what we are looking for - ignore it */
						outptr++[0] = inptr++[0];
						segLen--;
						bytesMoved++;
					}
				}
			}
			else
			{
				/* move the next byte of input */
				outptr++[0] = inptr++[0];
				segLen--;
				bytesMoved++;
			}
		}
	}

	/* insert padding characters if necessary */
	extra = bytesMoved % 4;
	if (extra > 0)
	{
		extra = 4 - extra;
	}

	while (extra > 0)
	{
		outptr++[0] = ' ';
		bytesMoved++;
		extra--;
	}

	/* return the new length */
	return bytesMoved;
}

int	BuildReply(char * msgdata, int datalen, char * inputData, int inputLen, MQMD *md, MQLONG qm, replyParms * parms)

{
	int		replyLen=datalen;
	int		rfhLen;
	int		segLen=0;
	int		outSegLen;
	int		totLen=0;
	char	*replyData=msgdata;
	char	*inptr;
	char	*outptr;
	MQRFH2	*rfh2;
	MQLONG	cc=MQCC_OK;
	MQLONG	rc=0;
	MQLONG	openopt=0;
	MQPMO	pmo = {MQPMO_DEFAULT};
	MQOD	od = {MQOD_DEFAULT};    /* Object Descriptor             */
	MQMD	mqmd = {MQMD_DEFAULT};
	char	qmName[MQ_Q_MGR_NAME_LENGTH + 8];
	char	qName[MQ_Q_NAME_LENGTH + 8];
	char	trimmedQMname[MQ_Q_MGR_NAME_LENGTH + 8];
	char	trimmedQname[MQ_Q_NAME_LENGTH + 8];

	memset(qmName, 0, sizeof(qmName));
	memset(qName, 0, sizeof(qName));

	/* check if this is a request message */
	if (md->MsgType != MQMT_REQUEST)
	{
		printf("Message type is not a request message - message not processed - type=%d\n", md->MsgType);
		return -1;
	}

	/* capture the reply to queue manager name */
	memcpy(qmName, md->ReplyToQMgr, MQ_Q_MGR_NAME_LENGTH);

	/* capture the reply to queue name */
	memcpy(qName, md->ReplyToQ, MQ_Q_NAME_LENGTH);

	/* get another copy of the queue name with trailing blanks removed for display purposes */
	strcpy(trimmedQname, qName);
	rtrim(trimmedQname);
	strcpy(trimmedQMname, qmName);
	rtrim(trimmedQMname);

	/* check if we are using the same queue as before */
	if (((strcmp(parms->replyQname, trimmedQname) != 0) || (strcmp(parms->replyQMname, trimmedQMname) != 0)) && (parms->replyQname[0] != 0) && (parms->replyQ != 0))
	{
		/* close the current reply to queue */
		MQCLOSE(qm, &(parms->replyQ), MQCO_NONE, &cc, &rc);
		parms->replyQ = 0;

		checkerror("MQCLOSE", cc, rc, parms->replyQMname);
	}

	/* make sure the previous connect has worked */
	if (0 == cc)
	{
		/* check if we want to introduce a delay - specified in milliseconds */
		if (parms->sleeptime > 0)
		{
			Sleep(parms->sleeptime);
		}

		/* remember the queue manager we are connected to */
		strcpy(parms->replyQMname, trimmedQMname);

		/* check if we need to open the reply queue */
		if (0 == parms->replyQ)
		{
			/* set the queue open options */
			openopt = MQOO_OUTPUT + MQOO_FAIL_IF_QUIESCING;

			/* set the queue name */
			strncpy(od.ObjectName, trimmedQname, MQ_Q_NAME_LENGTH);

			/* check if the queue manager is the same as the connected queue manager */
			if (strcmp(trimmedQMname, parms->qmname) != 0)
			{
				/* looks like a remote queue manager name */
				strncpy(od.ObjectQMgrName, trimmedQMname, MQ_Q_MGR_NAME_LENGTH);
			}

			if (0 == parms->silent)
			{
				/* tell what we are doing */
				printf("opening reply queue %s\n", trimmedQname);
			}

			/* open the queue for output */
			MQOPEN(qm, &od, openopt, &(parms->replyQ), &cc, &rc);

			/* check for errors */
			checkerror("MQOPEN", cc, rc, qName);
		}

		if (cc != MQCC_OK)
		{
			printf("Unable to open reply to queue %s for output\n", trimmedQname);
		}
		else
		{
			/* remember the queue we are connected to */
			strcpy(parms->replyQname, qName);

			/* set the MQMD options from the original message */
			memcpy(mqmd.CorrelId, md->MsgId, MQ_CORREL_ID_LENGTH);
			mqmd.MsgType = MQMT_REPLY;
			memcpy(mqmd.Format, parms->msgFormat, MQ_FORMAT_LENGTH);

			/* check if a ccsid was specified */
			if (parms->ccsid != 0)
			{
				mqmd.CodedCharSetId = parms->ccsid;
			}

			/* check if encoding was specified */
			if (parms->encoding != 0)
			{
				mqmd.Encoding = parms->encoding;
			}

			/* check if we need to return the RFH2 header from the request message */
			/* some programs like WBI/SF may require this */
			if (1 == parms->resendRFH)
			{
				/* check if we have an RFH2 header */
				if ((memcmp(md->Format, MQFMT_RF_HEADER_2, MQ_FORMAT_LENGTH) == 0) && (inputLen >= MQRFH_STRUC_LENGTH_FIXED_2))
				{
					rfh2 = (MQRFH2 *)inputData;
					rfhLen = rfh2->StrucLength;

					/* allocate memory to allocate for the reply */
					replyData = (char *)malloc(rfhLen + datalen);
					rfh2 = (MQRFH2 *)replyData;

					/* copy the fixed part of the RFH to the data buffer */
					memcpy(replyData, inputData, MQRFH_STRUC_LENGTH_FIXED_2);

					/* set the message format of the reply in the RFH2 header */
					memcpy(rfh2->Format, parms->msgFormat, 8);

					/* calculate the remaining length */
					rfhLen -= MQRFH_STRUC_LENGTH_FIXED_2;
					totLen = MQRFH_STRUC_LENGTH_FIXED_2;

					/* get an input and an output pointer */
					outptr = replyData + MQRFH_STRUC_LENGTH_FIXED_2;
					inptr = inputData + MQRFH_STRUC_LENGTH_FIXED_2;

					while (rfhLen > 0)
					{
						/* get the length of this segment */
						memcpy((char *)&segLen, inptr, 4);
						inptr += 4;

						/* figure out what kind of folder this is */
						if (memcmp(inptr, "<jms>", 5) == 0)
						{
							/* need to remove the Dst element and change the Rto to Dst */
							outSegLen = processJMSfolder(inptr, outptr + 4, segLen);
						}
						else
						{
							/* no special processing necessary - just copy the data */
							memcpy(outptr + 4, inptr, segLen);

							/* output length is the same as the input length */
							outSegLen = segLen;
						}

						/* set the segment length */
						memcpy(outptr, (char *)&outSegLen, 4);

						/* calculate the total length of the RFH2 */
						totLen += outSegLen + 4;

						/* update the input and output pointers */
						inptr += segLen;
						outptr += outSegLen + 4;

						/* calculate the remaining bytes in the input RFH2 */
						rfhLen -= segLen + 4;
					}

					/* set the total length in the RFH header */
					rfh2->StrucLength = totLen;
				}

				/* copy the message data */
				memcpy(replyData + totLen, msgdata, datalen);
			}
			else
			{
				replyData = msgdata;
			}

			/* write the reply message */
			/* perform the MQPUT */
			MQPUT(qm, parms->replyQ, &mqmd, &pmo, replyLen + totLen, replyData, &cc, &rc);

			/* check for errors */
			checkerror("MQPUT", cc, rc, parms->replyQname);

			if (0 == cc)
			{
				parms->replyCount++;

				if (0 == parms->silent)
				{
					printf("Reply sent to queue %s on queue manager %s\n", trimmedQname, trimmedQMname);
				}
			}

			/* check if we acquired storage */
			if ((1 == parms->resendRFH) && (memcpy(md->Format, MQFMT_RF_HEADER_2, MQ_FORMAT_LENGTH) == 0) && (datalen >= MQRFH_STRUC_LENGTH_FIXED_2))
			{
				/* free the storage we acquired */
				free(replyData);
			}
		}
	}

	return cc;
}

void InterruptHandler (int sigVal) 

{ 
	terminate = 1;
}

void printHelp(char *pgmName)

{
	printf("%s\n", Level);
	printf("format is:\n");
	printf("   %s -f parm_filename -r data_filename -l 0|1 -t milliseconds -m QMname -q queue -s\n", pgmName);
	printf("         -s will suppress normal operational messages\n");
	printf("         -l is logical order and is used with MQ message groups\n");
	printf("           it can be 1 (use logical order) or 0 (default - do not use)\n");
	printf("         -m will override the queue manager and -q will override the queue name\n");
	printf("         -t will override the sleep time parameter\n");
#ifdef MQCLIENT
	printf("         -m can be in the form of ChannelName/TCP/hostname(port)\n");
#endif
}

int main(int argc, char **argv)
{
	int		rc;
	int		msgcount=0;
	int		maxrate=0;
	int		firstsec=0;
	int		secondcount=0;
	int		totalbytes=0;
	int		avgbytes;
	int		firstTime=0;
	int		rfhlength=0;
	int		remainingTime;
	MQLONG	qm=0;
	MQLONG	q=0;
	MQLONG	compcode;
	MQLONG	reason;
	MQLONG	Select[1];			/* attribute selectors           */
	MQLONG	IAV[1];				/* integer attribute values      */
	MQOD	objdesc = {MQOD_DEFAULT};
	MQMD	msgdesc = {MQMD_DEFAULT};
	MQLONG	openopt = 0;
	MQGMO	mqgmo = {MQGMO_DEFAULT};
	MQLONG	datalen=0;
	char	*msgdata;
	FILE	*replyDataFile;
	char	*replyData=0;
	MQLONG	replyDataLen=0;
	MQOD	od = {MQOD_DEFAULT};    /* Object Descriptor             */
	replyParms	parms;					/* Input parameters and global variables */

#ifdef MQCLIENT
	printf("MQReply V1.01 (Client) program start\n");
#else
	printf("MQReply V1.01 program start\n");
#endif

	/* print the copyright statement */
	printf(copyright);

	/* initialize the work areas */
	memset(&parms, 0, sizeof(parms));
	parms.saveSleeptime = -1;
	parms.maxMsgLen = MAX_MESSAGE_LENGTH;
	parms.maxWaitTime = 120;
	memset(parms.msgFormat, 32, sizeof(parms.msgFormat));
	parms.msgFormat[MQ_FORMAT_LENGTH] = 0;

	/* check for too few input parameters */
	if (argc < 3)
	{
		printHelp(argv[0]);
		exit(99);
	}

	/* process any command line arguments */
	processArgs(argc, argv, &parms);

	if (parms.err != 0)
	{
		printHelp(argv[0]);
		exit(99);
	}

	if ((0 == parms.parmFilename[0]) || (0 == parms.replyFilename[0]))
	{
		printHelp(argv[0]);
		exit(99);
	}

	/* tell what parameters file we are using */
	printf("Reading parameters from file %s\n", parms.parmFilename);

	/* read the parameters file to get the options */
	rc = processParmFile(&parms);

	/* check for overrides */
	if (parms.saveQMname[0] != 0)
	{
		strcpy(parms.qmname, parms.saveQMname);
	}

	/* check for overrides */
	if (parms.saveQname[0] != 0)
	{
		strcpy(parms.qname, parms.saveQname);
	}

	/* check for override */
	if (parms.saveSleeptime >= 0)
	{
		parms.sleeptime = parms.saveSleeptime;
	}

	/* exit if queue name not found */
	if (0 == parms.qname[0])
	{
		printf("***** Queue name not found in parameters file\n");
		return 2;
	}

	/* return if unable to find parameters file */
	if (rc != 0)
	{
		return rc;
	}

	/* Tell what queue and qm will be used */
	if (0 == parms.qmname[0])
	{
		printf("Reading messages from Queue(%s) on default Qmgr\n", parms.qname);
	}
	else
	{
		printf("Reading messages from Queue(%s) on Qmgr(%s)\n", parms.qname, parms.qmname);
	}

	if (parms.sleeptime > 0)
	{
		/* print out the sleep time parameter */
		printf("Sleep time set to %d milliseconds\n", parms.sleeptime);
	}

	/* check for help request */
	if ((parms.replyFilename[0] == '?') || (parms.replyFilename[1] == '?'))
	{
		printHelp(argv[0]);
		exit(0);
	}

	/* try to read the reply data file */
	replyDataFile = fopen(parms.replyFilename, "rb");

	if (0 == replyDataFile)
	{
		/* error opening reply data file */
		printf("Unable to open reply data file %s\n", parms.replyFilename);
		exit(85);
	}

	/* determine the file length */
	/* figure out how long the data is and read it into a buffer */
	fseek(replyDataFile, 0L, SEEK_END);
	replyDataLen = ftell(replyDataFile);
	fseek(replyDataFile, 0L, SEEK_SET);
	replyData = (char *) malloc(replyDataLen + 1);

	/* read the data into the buffer */
	fread(replyData, 1, replyDataLen, replyDataFile);

	/* tell what we are doing */
	printf("\n%d bytes read from reply data file %s\n", replyDataLen, parms.replyFilename);


	/* close the reply data file */
	fclose(replyDataFile);

#ifdef WIN32
	/* set a termination handler */
	signal(SIGINT, InterruptHandler);
#endif

	/* Connect to the queue manager */
#ifdef MQCLIENT
	clientConnect2QM(parms.qmname, &qm, &(parms.maxMsgLen), &compcode, &reason);
#else
	connect2QM(parms.qmname, &qm, &compcode, &reason);
#endif

	/* check for errors */
	if (compcode != MQCC_OK)
	{
		return 98;
	}

	/* set the queue open options */
	strncpy(objdesc.ObjectName, parms.qname, MQ_Q_NAME_LENGTH);

	/* set the queue open options */
	openopt = MQOO_INPUT_SHARED + MQOO_FAIL_IF_QUIESCING + MQOO_INQUIRE;

	/* open the queue for input */
	printf("opening queue %s for input\n\n", parms.qname);
	MQOPEN(qm, &objdesc, openopt, &q, &compcode, &reason);

	/* check for errors */
	checkerror("MQOPEN", compcode, reason, parms.qname);
	if (compcode != MQCC_OK)
	{
		return 97;
	}

	/* check if our maximum message length is too large         */
	/* This is to avoid 2010 return codes on client connections */
	Select[0] = MQIA_MAX_MSG_LENGTH;
	IAV[0]=0;
	MQINQ(qm, q, 1L, Select, 1L, IAV, 0L, NULL, &compcode, &reason);

	if (IAV[0] < parms.maxMsgLen)
	{
		/* change the maximum message length to the maximum allowed */
		parms.maxMsgLen = IAV[0];
	}

	/* allocate a buffer for the message */
	msgdata = (char *)malloc(parms.maxMsgLen + 1);
	memset(msgdata, 0, parms.maxMsgLen + 1);

	/* enter get message loop */
	while ((compcode == MQCC_OK) && (0 == terminate))
	{
		/* set the get message options */
		if (0 == parms.maxWaitTime)
		{
			mqgmo.Options = MQGMO_NO_WAIT | MQGMO_FAIL_IF_QUIESCING;
		}
		else
		{
			mqgmo.Options = MQGMO_WAIT | MQGMO_FAIL_IF_QUIESCING;
			mqgmo.WaitInterval = parms.maxWaitTime * 1000;
		}

		mqgmo.MatchOptions = MQGMO_NONE;

		/* check if we are using logical order */
		if (1 == parms.logicalOrder)
		{
			mqgmo.Options |= MQGMO_LOGICAL_ORDER;
		}

		/* reset the msgid and correlid */
		memcpy(msgdesc.MsgId, MQMI_NONE, sizeof(msgdesc.MsgId));
		memcpy(msgdesc.CorrelId, MQCI_NONE, sizeof(msgdesc.CorrelId));

		remainingTime = parms.maxWaitTime;
		do
		{
			/* only wait for 1 second */
			mqgmo.WaitInterval = 1000;
			remainingTime--;

			/* since we have a signal handler installed, we do not want to be in an MQGET for a long time */
			/* perform the MQGET */
			MQGET(qm, q, &msgdesc, &mqgmo, parms.maxMsgLen, msgdata, &datalen, &compcode, &reason);
		} while ((remainingTime > 0) && (MQCC_FAILED == compcode) && (2033 == reason) && (0 == terminate));

		if (1 == terminate)
		{
			printf("\nProgram terminated by user\n");
		}
		else
		{
			if ((2 == compcode) && (2033 == reason))
			{
				printf("\nTimeout period expired - program is ending\n");
			}
			else
			{
				checkerror("MQGET", compcode, reason, parms.qname);
			}
		}

		if ((MQCC_OK == compcode) && (0 == terminate))
		{
			/* count the total number of messages read */
			msgcount++;

			/* calculate the total bytes in the message */
			totalbytes += datalen;

			/* generate a reply message */
			BuildReply(replyData, replyDataLen, msgdata, datalen, &msgdesc, qm, &parms);
		}
	}

	/* close the input queue */
	printf("closing the input queue\n");
	MQCLOSE(qm, &q, MQCO_NONE, &compcode, &reason);

	checkerror("MQCLOSE", compcode, reason, parms.qname);

	/* check if we have an open reply to queue */
	if (parms.replyQ != 0)
	{
		MQCLOSE(qm, &(parms.replyQ), MQCO_NONE, &compcode, &reason);

		checkerror("MQCLOSE", compcode, reason, parms.replyQname);
	}

	/* Disconnect from the queue manager */
	printf("disconnecting from the queue manager\n");
	MQDISC(&qm, &compcode, &reason);

	checkerror("MQDISC", compcode, reason, parms.qmname);

	/* dump out the total message count */
	printf("\nTotal messages processed %d\n", msgcount);

	/* give the total and average message size */
	if (msgcount > 0)
	{
		avgbytes = totalbytes / msgcount;
		printf("total bytes received in all messages %d\n", totalbytes);
		printf("average message size %d\n", avgbytes);
		printf("total replies sent %d\n", parms.replyCount);
	}

	/******************************************************************/
	/*                                                                */
	/* END OF PROGRAM                                                 */
	/*                                                                */
	/******************************************************************/

	printf("\nmqreply program ended\n");
	return 0;
}
