/********************************************************************/
/*                                                                  */
/*   MQCAPONE has 2 parameters                                      */
/*                                                                  */
/*      - name of the parameters file                               */
/*      - name of the file to store the captured messages           */
/*                                                                  */
/*    if no queue manager is specified, the default queue manager   */
/*    is used.                                                      */
/*                                                                  */
/********************************************************************/

/********************************************************************/
/*                                                                  */
/* Changes in V1.1                                                  */
/*                                                                  */
/* 1) Added support for Sun Solaris.                                */
/* 2) Use keyword rather than positional arguments.                 */
/*                                                                  */
/********************************************************************/

/********************************************************************/
/*                                                                  */
/* Changes in V1.3                                                  */
/*                                                                  */
/* 1) Added support for non-destructive gets.                       */
/* 2) Added support for capture of MQMD as well as message data     */
/* 3) Added maximum message length parameter override.              */
/*                                                                  */
/********************************************************************/

/********************************************************************/
/*                                                                  */
/* Changes in V1.3.1                                                */
/*                                                                  */
/* 1) Fixed bug where maxMsgLen was too large for client            */
/*    connections (2010 reason code on MQGET).                      */
/*                                                                  */
/********************************************************************/

/********************************************************************/
/*                                                                  */
/* Changes in V1.3.2                                                */
/*                                                                  */
/* 1) Added support for get by msgid, correlid and groupid          */
/* 2) Added check of completion code after MQINQ for max length.    */
/*                                                                  */
/********************************************************************/

/********************************************************************/
/*                                                                  */
/* Changes in V1.3.3                                                */
/*                                                                  */
/* 1) Added additional data display for 2080 reason code.           */
/*                                                                  */
/********************************************************************/

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

#ifdef SOLARIS
#include <ctype.h>
#include <unistd.h>
#endif

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

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

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

/* default buffer size is 16MB but will be adjusted to maximum length supported */
#define MAX_MESSAGE_LENGTH	256 * 65536

#define QNAME				"QNAME"
#define QMGR				"QMGR"
#define DELIMITER			"DELIMITER"
#define DELIMITERX			"DELIMITERX"
#define MSGID				"MSGID"
#define MSGIDX				"MSGIDX"
#define CORRELID			"CORRELID"
#define CORRELIDX			"CORRELIDX"
#define GROUPID				"GROUPID"
#define GROUPIDX			"GROUPIDX"
#define STRIPRFH			"STRIPRFH"
#define MAXMSGCOUNT			"MSGCOUNT"
#define SAVEMQMD			"SAVEMQMD"
#define READONLY			"READONLY"
#define MAXMSGLEN			"MAXMSGLEN"

#define RFHSTRIP			1
#define RFHNO				0

static char Copyright[] = "(C) Copyright IBM Corp, 2001/2002/2003/2004/2007";
static char Version[]=\
"@(#)MQCapone V1.33 - MQSI V2.0 MQ data capture tool  - Jim MacNair ";

#ifdef _DEBUG
	#ifdef MQCLIENT
		static char Level[]="mqcapone.c V1.33 Client Debug version ("__DATE__" "__TIME__")";
	#else
		static char Level[]="mqcapone.c V1.33 Debug version ("__DATE__" "__TIME__")";
	#endif
#else
	#ifdef MQCLIENT
		static char Level[]="mqcapone.c V1.33 Client Release version ("__DATE__" "__TIME__")";
	#else
		static char Level[]="mqcapone.c V1.33 Release version ("__DATE__" "__TIME__")";
	#endif
#endif

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

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

	/* Universal parameters */

	char	saveQname[MQ_Q_NAME_LENGTH + 1];
	char	saveQMname[MQ_Q_MGR_NAME_LENGTH + 1];
	int		saveMQMD;
	int		saveSaveMQMD;
	int		readOnly;
	int		saveReadOnly;
	int		maxMsgLen;
	int		msgidSet;
	int		correlidSet;
	int		groupidSet;

	/* MQMD parameters - per message */
	char	msgid[MQ_GROUP_ID_LENGTH + 1];
	char	correlid[MQ_CORREL_ID_LENGTH + 1];
	char	groupid[MQ_GROUP_ID_LENGTH + 1];

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

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

	int		striprfh;

	/* Queue and qmgr names to read from */
	char	qname[MQ_Q_NAME_LENGTH + 1];
	char	qmname[MQ_Q_MGR_NAME_LENGTH + 1];
} capParms;

void captureString(char * value, char * arg, const int maxlen, capParms * 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,
				  capParms * 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,
				  capParms * 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, capParms * parms)

{
	int		i;
	int		foundit;
	char	ch;

	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) && ('O' == ch))
			{
				foundit = 1;

				i = processIndArg(argc, 
								  argv, 
								  i,
								  parms->outputFilename, 
								  sizeof(parms->outputFilename), 
								  "name of output data file (-f)",
								  parms);
			}

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

			/* check for option */
			if ((0 == foundit) && ('R' == ch))
			{
				foundit = 1;
				parms->saveReadOnly = 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, capParms * 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 > MQ_Q_MGR_NAME_LENGTH)
		{
			/* parameter too long, issue warning and truncate */
			printf("***** Queue manager name - parameter too long (%d) - data truncated - (%s)\n", 
					len, value);

			len = MQ_Q_MGR_NAME_LENGTH;
		}

		/* 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);
	}

	/* recognize but ignore delimiter */
	if (strcmp(ptr, DELIMITER) == 0)
	{
		foundit = 1;
	}

	/* recognize but ignore delimiter */
	if (strcmp(ptr, DELIMITERX) == 0)
	{
		foundit = 1;
	}

	if (strcmp(ptr, MSGID) == 0)
	{
		foundit = 1;
		parms->msgidSet = 1;
		editCharParm(MSGID, parms->msgid, valueptr, sizeof(parms->msgid));
	}

	if (strcmp(ptr, MSGIDX) == 0)
	{
		foundit = 1;
		parms->msgidSet = 1;
		editHexParm(MSGIDX, parms->msgid, valueptr, sizeof(parms->msgid));
	}

	if (strcmp(ptr, CORRELID) == 0)
	{
		foundit = 1;
		parms->correlidSet = 1;
		editCharParm(CORRELID, parms->correlid, valueptr, sizeof(parms->correlid));
	}

	if (strcmp(ptr, CORRELIDX) == 0)
	{
		foundit = 1;
		parms->correlidSet = 1;
		editHexParm(CORRELIDX, parms->correlid, valueptr, sizeof(parms->correlid));
	}

	if (strcmp(ptr, GROUPID) == 0)
	{
		foundit = 1;
		parms->groupidSet = 1;
		editCharParm(GROUPID, parms->groupid, valueptr, sizeof(parms->groupid));
	}

	if (strcmp(ptr, GROUPIDX) == 0)
	{
		foundit = 1;
		parms->groupidSet = 1;
		editHexParm(GROUPIDX, parms->groupid, valueptr, sizeof(parms->groupid));
	}

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

		if (('y' == valueptr[0]) || ('Y' == valueptr[0]))
		{
			parms->striprfh = RFHSTRIP;
		}
		else
		{
			parms->striprfh = RFHNO;
		}
	}

	/* recognize but ignore message count */
	if (strcmp(ptr, MAXMSGCOUNT) == 0)
	{
		foundit = 1;
	}

	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, SAVEMQMD) == 0)
	{
		foundit = 1;

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

	if (strcmp(ptr, READONLY) == 0)
	{
		foundit = 1;
		parms->readOnly = 1;
	}

	return foundit;
}

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

void processParmLine(char * ptr, capParms * 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, capParms * 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(capParms * 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, MQMD *mqmd, capParms * parms)

{
	MQRFH2	tempRFH;
	char	tempEbcdic[32];
	int		rfhlength=0;
	int		rfhversion;
	int		ccsid;
	int		encoding;
	char	tempFormat[9];

	/* 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));
	memcpy(tempFormat, tempRFH.Format, 8);
	tempFormat[8] = 0;

	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);
		EbcdicToAscii((unsigned char *) &tempRFH.Format, sizeof(tempRFH.Format), (unsigned char *)tempFormat);
		memcpy(&tempRFH.StrucId, tempEbcdic, sizeof(tempRFH.StrucId));

		/* 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;
	ccsid = tempRFH.CodedCharSetId;
	encoding = tempRFH.Encoding;

	/* 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);
		ccsid = reverseBytes4(ccsid);
		encoding = reverseBytes4(encoding);

		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;
	}

	/* check if we are removing the RFH header and saving the MQMD */
	if ((1 == parms->saveMQMD) && (RFHSTRIP == parms->striprfh))
	{
		/* update the format, encoding and codepage fields in the MQMD */
		memcpy(mqmd->Format, tempRFH.Format, sizeof(mqmd->Format));
		if (ccsid > 0)
		{
			mqmd->CodedCharSetId = ccsid;
		}

		if (encoding > 0)
		{
			mqmd->Encoding = encoding;
		}
	}

	return rfhlength;
}

void printHelp(char *pgmName)

{
	printf("%s\n", Level);
	printf("format is:\n");
	printf("   %s -f parm-filename -o data_filename <-r> <-d> <-q qname> <-m qmgr>\n", pgmName);
	printf("      -r will use nondestructive reads\n");
	printf("      -d will save the MQMD with the data\n");
	printf("      -q will override the queue name\n");
	printf("      -m will override the queue manager name\n");
}

int main(int argc, char **argv)

{
	int			rc;
	int			msgcount=0;
	int			maxrate=0;
	int			firstsec=0;
	int			secondcount=0;
	int			totalbytes=0;
	int			firstTime=0;
	int			rfhlength=0;
	MQHCONN		qm=0;
	MQHOBJ		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;
	FILE		*outFile;
	char		*msgdata;
	char		*dataptr;
	capParms	parms;				/* input and command line parameters */

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

	/* initialize the work areas */
	memset(&parms, 0, sizeof(parms));
	parms.saveSaveMQMD = -1;
	parms.saveReadOnly = -1;
	parms.maxMsgLen = MAX_MESSAGE_LENGTH;

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

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

	/* 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.outputFilename[0]))
	{
		printHelp(argv[0]);
		exit(99);
	}

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

	/* read the ini file to get the delimiter string */
	rc = processParmFile(&parms);

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

	/* 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 overrides */
	if (1 == parms.saveReadOnly)
	{
		parms.readOnly = parms.saveReadOnly;
	}

	/* check for overrides */
	if (1 == parms.saveSaveMQMD)
	{
		parms.saveMQMD = parms.saveSaveMQMD;
	}

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

	if (RFHSTRIP == parms.striprfh)
	{
		printf("StripRFH option selected - RFH headers will be discarded if present\n");
	}

	/* tell if we are in browse mode vs normal get mode */
	if (1 == parms.readOnly)
	{
		printf("Readonly option selected - using browse - messages will be left on the queue\n");
	}

	/* 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);
	}

	/* open output file */
	printf("opening output file %s\n", parms.outputFilename);
	outFile = fopen(parms.outputFilename, "wb");

	if (NULL == outFile)
	{
		printf("unable to open output file %s for output\n", parms.outputFilename);
		return 100;
	}

	/* Connect to the queue manager */
#ifdef MQCLIENT
	clientConnect2QM(parms.qmname, &qm, &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);

	/* check if should use non-destructive gets */
	if (0 == parms.readOnly)
	{
		openopt = MQOO_INPUT_SHARED + MQOO_INQUIRE + MQOO_FAIL_IF_QUIESCING;
	}
	else
	{
		openopt = MQOO_BROWSE + MQOO_INQUIRE + MQOO_FAIL_IF_QUIESCING;
	}

	/* open the queue for input */
	printf("opening queue %s for input\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 ((0 == compcode) && (IAV[0] < parms.maxMsgLen) && (IAV[0] > 0))
	{
		/* change the maximum message length to the maximum allowed */
		parms.maxMsgLen = IAV[0];
	}
	else
	{
		checkerror("MQINQ", compcode, reason, parms.qname);
	}

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

	/* display the maximum message length that can be read */
	printf("Maximum message size that can be read is %d\n", parms.maxMsgLen);

	/* set the get message options */
	mqgmo.Version = MQGMO_VERSION_2;
	mqgmo.Options = MQGMO_NO_WAIT | MQGMO_FAIL_IF_QUIESCING;
	mqgmo.MatchOptions = MQGMO_NONE;

	/* check for non-destructive read */
	if (1 == parms.readOnly)
	{
		mqgmo.Options |= MQGMO_BROWSE_NEXT;
	}

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

	/* check if a msg id was specified */
	if (1 == parms.msgidSet)
	{
		memcpy(msgdesc.MsgId, parms.msgid, MQ_MSG_ID_LENGTH);
		mqgmo.MatchOptions |= MQMO_MATCH_MSG_ID;
 	}

	/* check if a correl id was specified */
	if (1 == parms.correlidSet)
	{
		memcpy(msgdesc.CorrelId, parms.correlid, MQ_CORREL_ID_LENGTH);
		mqgmo.MatchOptions |= MQMO_MATCH_CORREL_ID;
	}

	/* check if a group id was specified */
	if (1 == parms.groupidSet)
	{
		memcpy(msgdesc.GroupId, parms.groupid, MQ_GROUP_ID_LENGTH);
		mqgmo.MatchOptions |= MQMO_MATCH_GROUP_ID;
	}

	/* perform the MQGET */
	MQGET(qm, q, &msgdesc, &mqgmo, parms.maxMsgLen, msgdata, &datalen, &compcode, &reason);

	/* check for errors */
	checkerror("MQGET", compcode, reason, parms.qname);

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

		/* point to the data area */
		dataptr = msgdata;

		if (0 == parms.saveMQMD)
		{
			/* check for an MQMDE present */
			if ((datalen >= MQMDE_LENGTH_2) && (memcmp(dataptr, MQMDE_STRUC_ID, sizeof(MQMDE_STRUC_ID) - 1) == 0))
			{
				dataptr += MQMDE_LENGTH_2;
				datalen -= MQMDE_LENGTH_2;
				printf("MQMD extension removed from data\n");
			}
		}

		/* do we need to check for an RFH at the front of the data? */
		if (RFHSTRIP == parms.striprfh)
		{
			/* check the message format for an RFH */
			if ((memcmp(msgdesc.Format, MQFMT_RF_HEADER, sizeof(MQFMT_RF_HEADER) - 1) == 0) ||
				(memcmp(msgdesc.Format, MQFMT_RF_HEADER_2, sizeof(MQFMT_RF_HEADER_2) - 1) == 0))
			{
				rfhlength = checkRFH(dataptr, datalen, &msgdesc, &parms);
			}
		}

		/* should we include the MQMD in the file data? */
		if (1 == parms.saveMQMD)
		{
			fwrite(&msgdesc, sizeof(msgdesc), 1, outFile);
		}

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

		/* append the data to the file */
		fwrite(dataptr + rfhlength, datalen - rfhlength, 1, outFile);
	} 
	else if ((compcode == MQCC_WARNING) && (2080 == reason))
	{
		/* provide some additional details about the truncation */
		printf("***** Input buffer too short maxMsgLen=%d but messageLength=%d\n", parms.maxMsgLen, datalen);
	}

	/* close the file */
	fclose(outFile);

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

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

	/* 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("Total messages %d\n", msgcount);

	/* give the total and average message size */
	if (msgcount > 0)
	{
		printf("total bytes in message %d\n", totalbytes);
	}

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

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