/****************************************************************************/
/* Summary	: Command-line functions for BEDS Prototype on Persistor CF2	*/
/* Filename : bedsCmd.c														*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: BEDS (Benthic Event Detection System)							*/
/* Revision : 1.0															*/
/* Created	: 09/17/2012													*/
/****************************************************************************/
/* Modification History:													*/
/* 17sep2012 rah - created from Persistor "toPico" stationery and			*/
/*				   previous BEDS prototypes									*/
/* $Log$
 */
/****************************************************************************/

#include <mbariTypes.h>			/* MBARI type definitions				*/
#include <beds.h>				/* BEDS general definitions				*/
#include <bedsUI.h>				/* BEDS Command line processor			*/
#include <bedsCmd.h>			/* BEDS Command line processor			*/
#include <file.h>				/* BEDS file I/O definitions			*/
#include <fileUtils.h>			/* BEDS file utilities definitions		*/
#include <clock.h>				/* BEDS Clock module					*/
#include <variable.h>			/* Variable function definitions		*/
#include <syslog.h>				/* BEDS System logger					*/
#include <serial.h>				/* BEDS Serial I/O						*/
#include <utils.h>				/* BEDS Utility routines				*/
#include <picConfig.h>			/* For picSoftReset()					*/
#include <adc.h>				/* BEDS Analog Module					*/
#include <watch.h>				/* For sensorsOn()						*/
//#include "ZModem/bedszm.h"		/* ZModem definitions					*/
//#include <bedsym.h>				/* YModem definitions					*/
#include <getopt.h>

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


/****************************************/
/*		External Data					*/
/****************************************/

Extern Int32	sensorWarmup, platformID;
Extern char		*timespecErr;
Extern char		*watchIdxName, *eventIdxName;


/****************************************/
/*		Module Local Data				*/
/****************************************/

MLocal char		*idxFileErr = "Cannot open index file";
MLocal char		*oFileErr	= "Cannot create output file";
MLocal char		*noFileErr	= "No file name";
MLocal Nat16	oFileIndex = 1;
MLocal char		syslogBuf[CMDBUF_LEN];


/****************************************/
/*		Module Local Data				*/
/****************************************/

//MLocal char		*tmp1File = "INDEX1.TMP", *tmp2File = "INDEX2.TMP";


/************************************************************************/
/* Function	   : QuitCmd												*/
/* Purpose	   : Exit command line parser								*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : ABORT													*/
/************************************************************************/
CmdRtn quitCmd(int argc, char **argv)
{
	sysLog("Exiting command parser");
	return(ABORT);

} /* QuitCmd() */


/************************************************************************/
/* Function	   : DeployCmd												*/
/* Purpose	   : Go to deployment mode									*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
CmdRtn deployCmd(int argc, char **argv)
{
	int			opt, help=0, quit=0, n=0;

    while ((opt = getopt(argc, argv, "hnq")) != -1)
	{
        switch (opt)
		{
		  case 'h': help=1; break;
		  case 'n': n=1;; break;
		  case 'q': quit=1; break;
		}
	}

	if (help)
	{
		printf("\r\n%s [-q] [-n]\tGo to deployment mode\r\n"
			   "\t[-q] Just query current mode\r\n"
			   "\t[-n] Turn off deployment mode\r\n\n", argv[0]);
		return(OK);
	}

	if (n)
		setDeploymentMode(FALSE);
	else if (!quit)
		setDeploymentMode(TRUE);
	
	printf("\r\nDeployment mode = %s\r\n", getDeploymentMode() ? "ON" : "OFF");
	return(OK);
}


/************************************************************************/
/* Function	   : resetConfirm											*/
/* Purpose	   : Confirm user command									*/
/* Inputs	   : None													*/
/* Outputs	   : TRUE if user confirm, else FALSE						*/
/************************************************************************/
MBool resetConfirm(void)
{
	int		c;

	printf("Are you really sure?  ");
	c = ser_getc_tmout(getConsole(), 30*TICKS_PER_SECOND);
	newline();
	return((c == 'Y') || (c == 'y'));
}


/************************************************************************/
/* Function	   : resetCmd												*/
/* Purpose	   : Reset the system										*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : OK, if it ever returns at all							*/
/************************************************************************/
CmdRtn resetCmd(int argc, char **argv)
{
	printf("\r\nThis could cause permanent loss of communication.\r\n");
	if (!resetConfirm())
		return(OK);

	sysLog("Resetting system by user request");
	fileClose();

	picSoftReset();
	return(OK);
}


/************************************************************************/
/* Function	   : recoverCmd												*/
/* Purpose	   : Recover the system										*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : OK, if it ever returns at all							*/
/************************************************************************/
CmdRtn recoverCmd(int argc, char **argv)
{
	int			opt, help=0;

    while ((opt = getopt(argc, argv, "h")) != -1)
	{
        if (opt == 'h')
			help = 1;
	}

	if (help)
	{
		printf("\r\nRecover the system. Closes files and waits forever\r\n");
		return(OK);
	}

	if (resetConfirm())
	{
		fileClose();
		printf("Now power off\r\n");
		while(TRUE)
			;
	}

	return(OK);

} /* recoverCmd() */


/************************************************************************/
/* Function	   : syslogCmd												*/
/* Purpose	   : Command-line command to write to syslog				*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn syslogCmd(int argc, char **argv)
{
	int		i;

	newline();
	memset(syslogBuf, 0, sizeof(syslogBuf));
	
	for (i = 1; i < argc; i++)
		strcat(syslogBuf, argv[i]);

	sysLog(syslogBuf);
	return(OK);

} /* syslogCmd() */

/************************************************************************/
/* Function	   : timeCmd												*/
/* Purpose	   : Get/Set Time and Date									*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : OK														*/
/************************************************************************/
CmdRtn timeCmd(int argc, char **argv)
{
	time_t			oldSecs, newSecs;
	struct tm		*newtime;
	int				i, n, year, mon, day, hour, min, sec;
	MBool			changed = FALSE;

	oldSecs = newSecs = clkTime();
	newtime = gmtime(&oldSecs);					/* Init newtime in case	*/
												/* user input has no date*/
	for (i = 1; i < argc; i++)
	{
		if (sscanf(argv[i], " %u/%u/%u", &year, &mon, &day) >= 3)
		{
			if	(year < YEAR0)					/* If 2 digit year & < 70 */
				year += 100;					/*	use 21st century	  */
			else if (year >= 1900)				/* If 4 digit year, adjust*/
				year -= 1900;					/*for mktime (offset 1900)*/
			newtime->tm_year = year;			/* Copy into struct tm	  */
			newtime->tm_mon = mon -1;			/* Normalize month		  */
			newtime->tm_mday = day;	
			changed = TRUE;
		}
		else if ((n = sscanf(argv[i], " %u:%u:%u", &hour, &min, &sec)) >= 2)
		{
			newtime->tm_hour = hour;
			newtime->tm_min = min;
			newtime->tm_sec = (n > 2) ? sec : 0;
			changed = TRUE;
		}
	}

	if (changed)
		if ((newSecs = mktime(newtime)) != -1)
			clkSetTime(newSecs);

	clkPrintTime(newSecs);
	newline();

	return( OK );

} /* timeCmd() */


/************************************************************************/
/* Function	   : analogCmd												*/
/* Purpose	   : Command to read A/D channel(s)							*/
/* Inputs	   : CmdInfoPtr												*/
/* Outputs	   : NULL if OK, else error message							*/
/************************************************************************/
CmdRtn analogCmd(int argc, char **argv)
{
	Nat16		val;
	Flt32		engValue;
	char		*units;
	int			i, opt, chan=0, help=0, navg=1, nchans=1;

    while ((opt = getopt(argc, argv, "a:hn:")) != -1)
	{
        switch (opt)
		{
		  case 'h': help=1; break;
		  case 'a': navg = atoi(optarg); break;
		  case 'n': nchans = atoi(optarg); break;
		}
	}

	if (help)
	{
		printf("\r\nANALOG\tRead analog channel(s)\r\n"
			   "ANALOG [-a<n>] [-n<n>] [-p[n]] <chan>\r\n"
			   "\t [-a<n>] Average <n> samples\r\n"
			   "\t [-n<n>] Read <n> channels\r\n");
		return(OK);
	}

	if (argc > optind)
		chan = atoi(argv[optind]);
	else
		nchans = 6;

	for (i = chan; i < chan+nchans; i++)
	{
		if (navg > 1)
			val = adcSampleAvg(i, navg);
		else
			val = adcSample(i);
		engValue = adcEngValue(i, val, &units);
		printf("Channel %d = %u counts %5.2f volts %6.3f %s\r\n",
			   i, val, RAW_TO_VOLTS(val), engValue, units);
	}

	return(OK);

} /* AnalogCmd() */

#if 0
/************************************************************************/
/* Function	   : sendEvent												*/
/* Purpose	   : Send a compressed event file via YModem or ZModem		*/
/* Inputs	   : Evt num, split factor, decimate factor, protocol		*/
/* Outputs	   : Errno													*/
/************************************************************************/
MLocal char *sendEvent(long eventNum, int split, int decimate,
					   SendProtocol protocol)
{
	int			result = 0;
	int			num;
	char		fname[32], suffix[8];
	static		char *protErr = "Protocol Error";

	getDataFileName(eventNum, "EVT", fname);

	if (((decimate > 1) || (split > 1)) &&
		(splitAndDecimate(fname, split, decimate) == OK))
	{
		for (num = 0; num < split; num++)
		{
			sprintf(suffix, "E%02u", num);
			getDataFileName(eventNum, suffix, fname);

			switch(protocol)
			{
			  case YMODEM:
				result = ymSendFiles(fname);
				break;

			  default:
				result = zmSendFiles(fname, 0, 1024, 0);
			}

			printf("\n%s: result = %d\n", fname, result);

			if (result == OK)
			{
				printf("Erasing %s\n", fname);
				unlink(fname);
			}
			else
				return(protErr);
		}
	}
	else
	{
		switch(protocol)
		{
		  case YMODEM:
			result = ymSendFiles(fname);
			break;

		  default:
			result = zmSendFiles(fname, 0, 1024, 0);
		}
	}

	return(result==0 ? NULL : protErr);

} /* sendEvent() */


/************************************************************************/
/* Function	   : displayIndexEntry										*/
/* Purpose	   : Display one index entry								*/
/* Inputs	   : IdxRcd pointer											*/
/* Outputs	   : None													*/
/************************************************************************/
MLocal void displayIndexEntry(IdxRcd *idxp)
{
	struct tm	*tmp;
	int			fileNum;
	Flt32		fmaxVal;

	tmp = gmtime(&idxp->startTime);
	fmaxVal = (float)(idxp->maxVal) / 65536.0f;
	idxp->fileName[15]='\0';
	if (sscanf(&idxp->fileName[3], "%ld", &fileNum) <= 0)
		fileNum = -1;

	printf("%4d	 %04d/%02d/%02d %02d:%02d:%02d.%03hd %4ld.%03lu %7.4f  %s\n",
		   fileNum, tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
		   tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
		   idxp->startMs, idxp->duration/1000, idxp->duration%1000,
		   fmaxVal, idxp->fileName);

} /* displayIndexEntry() */


/************************************************************************/
/* Function	   : showIndexFunc											*/
/* Purpose	   : Function for more() to show one index entry			*/
/* Inputs	   : Line number, IndexStruct								*/
/* Outputs	   : TRUE if found entry, else FALSE						*/
/************************************************************************/
MLocal MBool showIndexFunc(int lineNo, IndexStruct *ip)
{
	if (lineNo >= ip->entries)
		return(FALSE);

	displayIndexEntry(&ip->index[lineNo]);

	return(TRUE);

} /* showIndexFunc() */


/************************************************************************/
/* Function	   : showIndexAltFunc										*/
/* Purpose	   : Alternate function for more() to show one index entry	*/
/* Inputs	   : Line number, FILE pointer								*/
/* Outputs	   : TRUE if found entry, else FALSE						*/
/************************************************************************/
MLocal MBool showIndexAltFunc(int lineNo, FILE *fp)
{
	IdxRcd		idxRec;

	if (!getNextRec(fp, IndexType, &idxRec))
		return(FALSE);

	displayIndexEntry(&idxRec);

	return(TRUE);

} /* showIndexAltFunc() */


/************************************************************************/
/* Function	   : showIndexEntries										*/
/* Purpose	   : Display IndexRcd entries for .IDX file					*/
/* Inputs	   : Index file name, compare func for ordering, num lines for more*/
/* Outputs	   : NULL or error string									*/
/************************************************************************/
MLocal char *showIndexEntries(char *fname, MBool (*compareFunc)(), int numLines)
{
	IndexStruct *ip;
	FILE		*fp;

	if ((ip = createSortedIndex(fname, compareFunc)) == NULL)
	{
		if ((fp = fopen(fname, "r")) == NULL)
			return(idxFileErr);
		printf("\nProblem opening or sorting index file.  Printing in unsorted time order.\n");
	}

	printf("\nEvent		   Start Time		 Duration  Max G  File Name\n");

	if (ip == NULL)
	{
		more(numLines, showIndexAltFunc, fp);
		fclose(fp);
	}
	else
	{
		more(numLines, showIndexFunc, ip);
		free(ip);
	}

	return(NULL);

} /* showIndexEntries() */


/************************************************************************/
/* Function	   : EventCmd												*/
/* Purpose	   : Command-line command to display and send events		*/
/* Inputs	   : CmdInfoPtr												*/
/* Outputs	   : NULL if OK, else error message							*/
/************************************************************************/
char *EventCmd(CmdInfoPtr cip)
{
	long		i, sends = 0;
	char		*result;
	int			n = 10, dec = 1, split = 1;
	SendProtocol protocol = ZMODEM;

	DosSwitch	qsw = { "/", '?', 0, 0 };				// help
	DosSwitch	nsw = { "/-", 'N', 0, "%ld", false };
	DosSwitch	dsw = { "/-", 'D', 0, "%ld", false };
	DosSwitch	ssw = { "/-", 'S', 0, "%ld", false };
	DosSwitch	tsw = { "/-", 'T', 0, 0 };
	DosSwitch	ksw = { "/-", 'K', 0, 0 };
	DosSwitch	ysw = { "/-", 'Y', 0, 0 };

	CmdExtractCIDosSwitches(cip, "1234567", &qsw, &nsw, &dsw, &ssw, &tsw, &ksw, &ysw);

	printf("\n");

// HELP SWITCH SELECTED (common format to many commands)
	if (qsw.pos)
	{
		printf("EVENT [/T] [/N<n>] to display events\n"
			   "\t[/T] display in reverse time order (defaults to magnitude order)\n"
			   "\t[/N<n>] display <n> lines at a time (default 10)\n"
			   "EVENT [/D<dec>] [/S<sp>] [n] to send event [n] via ZModem or Kermit\n"
			   "\t[/D<dec>] decimate value by factor <dec>\n"
			   "\t[/S<sp>]	split file <sp> ways (2-99)\n"
			   "\t[/K] to send via Kermit (dflt is ZModem)\n"
			   "\t[/Y] to send via YModem (dflt is ZModem)\n\n");
		return(NULL);
	}

	if (nsw.pos && nsw.hasv)
		n = (int)nsw.lv;

	if (dsw.pos && dsw.hasv)
		dec = (int)dsw.lv;

	if (ssw.pos && ssw.hasv)
	{
		split = (int)ssw.lv;
		if (split > 99)
			split = 99;
		if (split < 1)
			split = 1;
	}

	if (ksw.pos)
		protocol = KERMIT;
	else if (ysw.pos)
		protocol = YMODEM;

	for (i = 1; i < cip->argc; i++)
		if (cip->argv[i].isval)
		{
			if ((result = sendEvent(cip->argv[i].value, split, dec, protocol)) != NULL)
				return(result);
			sends++;
		}

	if (sends == 0)
		return(showIndexEntries(eventIdxName,
								tsw.pos ? trueFunc : magnitudeFunc, n));

	return(NULL);

} /* EventCmd() */


/************************************************************************/
/* Function	   : WatchCmd												*/
/* Purpose	   : Command-line command to display watch cycles			*/
/* Inputs	   : CmdInfoPtr												*/
/* Outputs	   : NULL if OK, else error message							*/
/************************************************************************/
char *WatchCmd(CmdInfoPtr cip)
{
	int			n = 10;

	DosSwitch	qsw = { "/", '?', 0, 0 };				// help
	DosSwitch	nsw = { "/-", 'N', 0, "%ld", false };

	CmdExtractCIDosSwitches(cip, twoArgs, &qsw, &nsw);

	if (qsw.pos)
	{
		printf("\nWATCH [/N<n>] to display watch cycles\n"
			   "\t[/N<n>] display <n> lines at a time (default 10)\n\n");
		return(NULL);
	}

	if (nsw.pos && nsw.hasv)
		n = nsw.lv;

	return(showIndexEntries(watchIdxName, trueFunc, n));

} /* WatchCmd() */


/************************************************************************/
/* Function	   : DecimateCmd											*/
/* Purpose	   : Command-line Decimate and split files					*/
/* Inputs	   : CmdInfoPtr												*/
/* Outputs	   : NULL if OK, else error message							*/
/************************************************************************/
char *DecimateCmd(CmdInfoPtr cip)
{
	long		dec=1, split=1;
	int			index;
	char		*rtn;

	DosSwitch	qsw = { "/", '?', 0, 0 };				// help
	DosSwitch	dsw = { "/-", 'D', 0, "%ld", false };
	DosSwitch	ssw = { "/-", 'S', 0, "%ld", false };

	CmdExtractCIDosSwitches(cip, threeArgs, &qsw, &dsw, &ssw);

	if (qsw.pos)
	{
		printf("\n%s [/D<dn>] [/S<sn>] <filename> Decimate and/or split file\n"
			   "\t[/D<dn>] to decimate file by factor of <dn>.\n"
			   "\t\tE.g., if <dn>=10, decimate 50Hz event to 5Hz\n"
			   "\t[/S<sn>] to split the file into <sn> parts\n"
			   "\t<filename> File to decimate or split.\n",
			   cip->argv[0].str);
		return(NULL);
	}

	if (dsw.pos && dsw.hasv)
		dec = dsw.lv;

	if (ssw.pos && ssw.hasv)
		split = ssw.lv;

	index = argIndex(3, &qsw, &dsw, &ssw);

	if (index >= cip->argc)
		return(noFileErr);

	printf("\nDecimate/split file %s, dec=%d, split=%d\n", cip->argv[index].str, dec, split);
	RTCDelayMicroSeconds(1000000);

	rtn = splitAndDecimate(cip->argv[index].str, split, dec);

	printf("done\n");
	return(rtn);

} /* DecimateCmd() */


/************************************************************************/
/* Function	   : SysRecCmd												*/
/* Purpose	   : Command-line command to display system records			*/
/* Inputs	   : CmdInfoPtr												*/
/* Outputs	   : NULL if OK, else error message							*/
/************************************************************************/
char	*SysRecCmd(CmdInfoPtr cip)
{
	time_t		startTime, now, nextSysRec;
	Int32		duration, interval;
	FILE		*ifp, *dfp, *ofp = NULL;
	int			recType;
	IdxRcd		idxRec;
	SystemRcd	sysRec;
	FileHdr		hdr;
	struct tm	*tmp;
	short		result;
	char		b[32], fname[32];

	DosSwitch	qsw = { "/-", '?', 0, 0 };				// help
	DosSwitch	ssw = { "/-", 'S', 0, "%s", false };
	DosSwitch	dsw = { "/-", 'D', 0, "%s", false };
	DosSwitch	tsw = { "/-", 'T', 0, "%s", false };
	DosSwitch	ysw = { "/-", 'Y', 0, 0, 0};

	CmdExtractCIDosSwitches(cip, fiveArgs, &qsw, &ssw, &dsw, &tsw, &ysw);

	printf("\n");
	if (qsw.pos)
	{
		printf("\n%s\tGet System Records\n"
			   "%s [/S <timespec>] [/D <n[s|m|h|d]>] [/Y]\n"
			   "\t [/S <timespec>] Starting time\n"
			   "\t [/D <n[s|m|h|d]>] Duration in seconds|minutes|hours|days\n"
			   "\t [/T <n[s|m|h|d]>] Display one sysrec every n seconds|minutes|hours|days\n"
			   "\t [/Y] Send as file via Ymodem\n",
			   cip->argv[0].str, cip->argv[0].str);
		return(NULL);
	}

	now = clkTime();

	if (!dsw.pos || ((duration = getDuration(dsw.sp)) <= 0))
		duration = 14400;

	if (!ssw.pos || ((startTime = getTimespec(ssw.sp)) <= 0))
		startTime = now - duration;

	nextSysRec = startTime;

	if (!tsw.pos || ((interval = getDuration(tsw.sp)) <= 0))
		interval = 1;

	if ((ifp = fopen(watchIdxName, "r")) == NULL)
		return(idxFileErr);

	if (ysw.pos)
	{
		sprintf(fname, "SR%04d.DAT", oFileIndex++);
		if ((ofp = fopen(fname, "w")) == NULL)
			return(oFileErr);
		hdr.recType = FileHdrType;
		hdr.rsvd = 0;
		hdr.fmtVersion = FMT_VERSION;
		hdr.platformID = (Nat16)(platformID & 0xffff);
		hdr.dataRate = 0;
		hdr.startTime = startTime;
		hdr.startMs = 0;
		hdr.duration = duration;
		fwrite(&hdr, sizeof(hdr), 1, ofp);
	}
	else
	{
		tmp = gmtime(&startTime);
		printf("Returning sys recs starting %04d/%02d/%02d %02d:%02d:%02d duration %ld interval %ld\n",
			   tmp->tm_year+1900, tmp->tm_mon+1, tmp->tm_mday,
			   tmp->tm_hour, tmp->tm_min, tmp->tm_sec, duration, interval);
	}

	while (getNextRec(ifp, IndexType, &idxRec))
	{
		if ((idxRec.startTime <= (startTime + duration)) &&
			((idxRec.startTime + idxRec.duration/1000) >= nextSysRec))
		{								/* Got a watch file in correct time */
			idxRec.fileName[15]='\0';
			if ((dfp = fopen(idxRec.fileName, "r")) != NULL)
			{
				while (getNextRec(dfp, SysRecType, &sysRec))
				{
					if (sysRec.rcdTime > (startTime + duration))
						break;
					if (sysRec.rcdTime >= nextSysRec)
					{
						if (ysw.pos)
							fwrite(&sysRec, sizeof(sysRec), 1, ofp);
						else
						{
							tmp = gmtime(&sysRec.rcdTime);
							printf("%02d/%02d/%04d %02d:%02d:%02d Batt %5.2fV "
								   "Vmodem %5.2fV ExtPress %6.3f bar IntPress %5.2f psia "
								   "Temp %4.1f degC Hum %4.1f%%\n",
								   tmp->tm_mon+1, tmp->tm_mday, tmp->tm_year+1900,
								   tmp->tm_hour, tmp->tm_min, tmp->tm_sec,
								   RAW_TO_BATTVOLTS(sysRec.battVoltage),
								   RAW_TO_BATTVOLTS(sysRec.minModemBatt),
								   adcEngValue(EXT_PRESS_CHAN, sysRec.extPressure, NULL),
								   adcEngValue(INT_PRESS_CHAN, sysRec.intPressure, NULL),
								   adcEngValue(TEMP_CHAN, sysRec.intTemp, NULL),
								   adcEngValue(HUMIDITY_CHAN, sysRec.intHumidity, NULL));
						}

						nextSysRec = sysRec.rcdTime + interval;
					}
					if (kbhit())
						break;
				}
				fclose(dfp);
			}
		}

		if (kbhit())
			break;
	}

	fclose(ifp);

	if (ysw.pos)
	{
		fclose(ofp);
		sprintf(b, "YS %s", fname);
		printf("\nSet your terminal program to receive YModem\n");
		CIOdrain();
		result = execstr(b);
		sysLogPrintf("Sent %s via YModem, result = %d", fname, result);
		unlink(fname);
	}

	return(NULL);

} /* SysRecCmd() */


/************************************************************************/
/* Function	   : ZSCmd													*/
/* Purpose	   : ZModem Send with Wildcards Command						*/
/* Inputs	   : CmdInfoPtr												*/
/* Outputs	   : NULL if OK, else error message							*/
/************************************************************************/
char *ZSCmd(CmdInfoPtr cip)
{
	int			index, rtn, verbose=0;
	unsigned	window=1024, framelen=0;

	DosSwitch	qsw = { "/-", '?', 0, 0 };
	DosSwitch	vsw = { "/-", 'V', 0, "%ld", false };
	DosSwitch	wsw = { "/-", 'W', 0, "%lu", false };
	DosSwitch	lsw = { "/-", 'L', 0, "%lu", false };

	CmdExtractCIDosSwitches(cip, fourArgs, &qsw, &vsw, &wsw, &lsw);

	printf("\n");

// HELP SWITCH SELECTED (common format to many commands)
	if (qsw.pos)
	{
		printf("%s [/V<n>] [/W<n>] [/L<n>] - Send file via ZModem protocol\n"
			   "\t [/V<n>] Verbosity level (0-8)\n"
			   "\t [/W<n>] Window size (>= 256)\n"
			   "\t [/L<n>] Frame length (32<=n<=1024)\n",
			   cip->argv[0].str);
		return(NULL);
	}

	if (cip->argc < 2)
		return("No file specified");

	if (vsw.pos && vsw.hasv)
		verbose = vsw.lv;

	if (wsw.pos && wsw.hasv)
		window = wsw.lv;

	if (lsw.pos && lsw.hasv)
		framelen = lsw.lv;

	for(index = argIndex(4, &qsw, &vsw, &wsw, &lsw); index < cip->argc; index++)
		if ((rtn = zmSendFiles(cip->argv[index].str, verbose, window, framelen)) != OK)
			break;

	return((rtn == OK) ? NULL : "ZModem error");
							  
} /* ZSCmd() */


/************************************************************************/
/* Function	   : YSCmd													*/
/* Purpose	   : YModem Send with Wildcards Command						*/
/* Inputs	   : CmdInfoPtr												*/
/* Outputs	   : NULL if OK, else error message							*/
/************************************************************************/
char *KSCmd(CmdInfoPtr cip)
{
	int			index, rtn;
	int			xfermode=0, errorrate=0, dbg=0;

	DosSwitch	qsw = { "/-", '?', 0, 0 };
	DosSwitch	bsw = { "/-", 'B', 0, 0, 0};
	DosSwitch	tsw = { "/-", 'T', 0, 0, 0};
	DosSwitch	esw = { "/-", 'E', 0, "%ld", false };
	DosSwitch	dsw = { "/-", 'D', 0, 0, 0};

	CmdExtractCIDosSwitches(cip, fiveArgs, &qsw, &bsw, &tsw, &esw, &dsw);

	printf("\n");

// HELP SWITCH SELECTED (common format to many commands)
	if (qsw.pos)
	{
		printf("%s [/B] [/T] [/D] [/E<n>] - Send file via Kermit protocol\n"
			   "\t [/B] Force binary transfer mode\n"
			   "\t [/T] Force text (ASCII) transfer mode\n"
			   "\t [/E<n>] Simulated error rate (0-100)\n"
			   "\t [/D] Turn on debugging\n",
			   cip->argv[0].str);
		return(NULL);
	}

	if (cip->argc < 2)
		return("No file specified");

	if (bsw.pos)
		xfermode = 1;
	
	if (tsw.pos)
		xfermode = 2;
	
	if (dsw.pos)
		dbg = 1;
	
	if (esw.pos && esw.hasv)
		errorrate = esw.lv;
	
	for(index = argIndex(4, &qsw, &bsw, &tsw, &esw, &dsw); index < cip->argc; index++)
		if ((rtn = kermitSendFiles(cip->argv[index].str, xfermode, errorrate, dbg)) != OK)
			break;

	return((rtn == OK) ? NULL : "Kermit error");
							  
} /* KSCmd() */


#endif

#ifdef DEBUG_TIME

/************************************************************************/
/* Function	   : TimeTestCmd											*/
/* Purpose	   : Command-line command to test time parsing				*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
CmdRtn timeTestCmd(int argc, char **argv)
{
	time_t		t;
	struct tm	*tmp;

	if (argc < 2)
		printf("\r\nTest by passing strings of the form mm/dd/yyyyThh:mm:ss\r\n");
	else
	{
		printf("\r\ngetTimespec(\"%s\") returns ", argv[1]);
		if ((t = getTimespec(argv[1])) == 0)
			printf("%s\r\n", timespecErr);
		else if ((tmp = gmtime(&t)) == NULL)
			printf("%d, but gmtime() returns ERROR\r\n", t);
		else
			printf("%d = %d/%d/%d %02d:%02d:%02d\r\n",
				   t, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_year+1900,
				   tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
	}

	return(OK);

} /* TimeTestCmd() */


/************************************************************************/
/* Function	   : DurTestCmd												*/
/* Purpose	   : Command-line command to test time parsing				*/
/* Inputs	   : argc, argv												*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
CmdRtn durTestCmd(int argc, char **argv)
{
	if (argc < 2)
		printf("\nusage: DurTest <n>\r\n");
	else
		printf("\r\n\ngetDuration(\"%s\") returns %d\r\n",
			   argv[1], getDuration(argv[1]));

	return(OK);

} /* DurTestCmd() */

#endif /* DEBUG_TIME */
