// ----------------------------------------------------------------------------
// macros.cxx
//
// Copyright (C) 2007-2010
//		Dave Freese, W1HKJ
// Copyright (C) 2008-2010
//		Stelios Bounanos, M0GLD
// Copyright (C) 2009
//		Chris Sylvain, KB3CS
//
// This file is part of fldigi.
//
// Fldigi is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Fldigi is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------

#include <config.h>

#include "macros.h"

#include "gettext.h"
#include "main.h"

#include "fl_digi.h"
#include "configuration.h"
#include "confdialog.h"
#include "logger.h"
#include "newinstall.h"
#include "globals.h"
#include "debug.h"
#include "status.h"
#include "trx.h"
#include "modem.h"
#include "qrunner.h"
#include "waterfall.h"
#include "rigsupport.h"
#include "network.h"
#include "logsupport.h"
#include "icons.h"

#include <FL/Fl.H>
#include <FL/filename.H>
#include "fileselect.h"

#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <string>
#include <fstream>
#include <queue>

#ifdef __WIN32__
#include "speak.h"
#endif

//using namespace std;

struct CMDS { std::string cmd; void (*fp)(std::string); };
static queue<CMDS> cmds;

// following used for debugging and development
//void pushcmd(CMDS cmd)
//{
//	LOG_INFO("%s, # = %d", cmd.cmd.c_str(), (int)cmds.size());
//	cmds.push(cmd);
//}
#define pushcmd(a) cmds.push((a))

// these variables are referenced outside of this file
MACROTEXT macros;
CONTESTCNTR contest_count;

std::string qso_time = "";
std::string qso_exchange = "";
std::string exec_date = "";
std::string exec_time = "";
std::string exec_string = "";
std::string info1msg = "";
std::string info2msg = "";
std::string text2repeat = "";

size_t repeatchar = 0;

bool macro_idle_on = false;

static float  idleTime = 0;
static bool TransmitON = false;
static bool ToggleTXRX = false;
static int mNbr;

static std::string text2send = "";

static size_t  xbeg = 0, xend = 0;

static bool save_xchg;
static bool expand;
static bool GET = false;
static bool timed_exec = false;
static bool within_exec = false;

static const char cutnumbers[] = "T12345678N";
static std::string cutstr;

static std::string cut_string(const char *s)
{
	cutstr = s;
	if (!progdefaults.cutnbrs || active_modem != cw_modem)
		return cutstr;

	for (size_t i = 0; i < cutstr.length(); i++)
		if (cutstr[i] >= '0' && cutstr[i] <= '9')
			cutstr[i] = cutnumbers[cutstr[i] - '0'];
	return cutstr;

}

static size_t mystrftime( char *s, size_t max, const char *fmt, const struct tm *tm) {
	return strftime(s, max, fmt, tm);
}

static void pFILE(std::string &s, size_t &i, size_t endbracket)
{
	std::string fname = s.substr(i+6, endbracket - i - 6);
	if (fname.length() > 0 && !within_exec) {
		FILE *toadd = fopen(fname.c_str(), "r");
		if (toadd) {
			std::string buffer;
			char c = getc(toadd);
			while (c && !feof(toadd)) {
				if (c != '\r') buffer += c; // damn MSDOS txt files
				c = getc(toadd);
				}
			s.replace(i, endbracket - i + 1, buffer);
			fclose(toadd);
		} else {
			LOG_WARN("%s not found", fname.c_str());
			s.replace(i, endbracket - i + 1, "");
		}
	} else
		s.replace(i, endbracket - i + 1, "");
}

static void pTIMER(std::string &s, size_t &i, size_t endbracket)
{
	int number;
	std::string sTime = s.substr(i+7, endbracket - i - 7);
	if (sTime.length() > 0 && !within_exec) {
		sscanf(sTime.c_str(), "%d", &number);
		progStatus.timer = number;
		progStatus.timerMacro = mNbr;
	}
	s.replace(i, endbracket - i + 1, "");
}

static void pREPEAT(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	progStatus.repeatMacro = mNbr;
	s.replace(i, endbracket - i + 1, "");
	text2repeat = s;
	repeatchar = 0;
	s.insert(i, "[REPEAT]");
}

static void pWPM(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	int number;
	std::string sTime = s.substr(i+5, endbracket - i - 5);
	if (sTime.length() > 0) {
		sscanf(sTime.c_str(), "%d", &number);
		if (number < 5) number = 5;
		if (number > 200) number = 200;
		progdefaults.CWspeed = number;
		sldrCWxmtWPM->value(number);
	}
	s.replace(i, endbracket - i + 1, "");
}

static void pRISETIME(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	float number;
	std::string sVal = s.substr(i+6, endbracket - i - 6);
	if (sVal.length() > 0) {
		sscanf(sVal.c_str(), "%f", &number);
		if (number < 0) number = 0;
		if (number > 20) number = 20;
		progdefaults.CWrisetime = number;
		cntCWrisetime->value(number);
	}
	s.replace(i, endbracket - i + 1, "");
}

static void pPRE(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	float number;
	std::string sVal = s.substr(i+5, endbracket - i - 5);
	if (sVal.length() > 0) {
		sscanf(sVal.c_str(), "%f", &number);
		if (number < 0) number = 0;
		if (number > 20) number = 20;
		progdefaults.CWpre = number;
		cntPreTiming->value(number);
	}
	s.replace(i, endbracket - i + 1, "");
}

static void pPOST(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	float number;
	std::string sVal = s.substr(i+6, endbracket - i - 6);
	if (sVal.length() > 0) {
		sscanf(sVal.c_str(), "%f", &number);
		if (number < -20) number = -20;
		if (number > 20) number = 20;
		progdefaults.CWpost = number;
		cntPostTiming->value(number);
	}
	s.replace(i, endbracket - i + 1, "");
}

static void setwpm(int d)
{
	sldrCWxmtWPM->value(d);
	cntCW_WPM->value(d);
}

static void doWPM(std::string s)
{
	int number;
	std::string sTime = s.substr(6);
	if (sTime.length() > 0) {
		sscanf(sTime.c_str(), "%d", &number);
		if (number < 5) number = 5;
		if (number > 200) number = 200;
		progdefaults.CWspeed = number;
		REQ(setwpm, number);
	}
}

static void pQueWPM(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doWPM };
	pushcmd(cmd);
	s.replace(i, endbracket - i + 1, "^!");
}

static void setRISETIME(int d)
{
	cntCWrisetime->value(d);
}

static void doRISETIME(std::string s)
{
	float number;
	std::string sVal = s.substr(7, s.length() - 8);
	if (sVal.length() > 0) {
		sscanf(sVal.c_str(), "%f", &number);
		if (number < 0) number = 0;
		if (number > 20) number = 20;
		progdefaults.CWrisetime = number;
		REQ(setRISETIME, number);
	}
}

static void pQueRISETIME(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRISETIME };
	pushcmd(cmd);
	s.replace(i, endbracket - i + 1, "^!");
}

static void setPRE(int d)
{
	cntPreTiming->value(d);
}

static void doPRE(std::string s)
{
	float number;
	std::string sVal = s.substr(6, s.length() - 7);
	if (sVal.length() > 0) {
		sscanf(sVal.c_str(), "%f", &number);
		if (number < 0) number = 0;
		if (number > 20) number = 20;
		progdefaults.CWpre = number;
		REQ(setPRE, number);
	}
}

static void pQuePRE(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doPRE };
	pushcmd(cmd);
	s.replace(i, endbracket - i + 1, "^!");
}

static void setPOST(int d)
{
	cntPostTiming->value(d);
}

static void doPOST(std::string s)
{
	float number;
	std::string sVal = s.substr(7, s.length() - 8);
	if (sVal.length() > 0) {
		sscanf(sVal.c_str(), "%f", &number);
		if (number < -20) number = -20;
		if (number > 20) number = 20;
		progdefaults.CWpost = number;
		REQ(setPOST, number);
	}
}

static void pQuePOST(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doPOST };
	pushcmd(cmd);
	s.replace(i, endbracket - i + 1, "^!");
}

static void pIDLE(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	float number;
	std::string sTime = s.substr(i+6, endbracket - i - 6);
	if (sTime.length() > 0) {
		sscanf(sTime.c_str(), "%f", &number);
		macro_idle_on = true;
		idleTime = number;
	}
	s.replace(i, endbracket - i + 1, "");
}

static void doneIDLE(void *)
{
	Qidle_time = 0;
}

static void doIDLE(std::string s)
{
	float number;
	std::string sTime = s.substr(7, s.length() - 8);
	if (sTime.length() > 0) {
		sscanf(sTime.c_str(), "%f", &number);
		Qidle_time = 1;
		Fl::add_timeout(number, doneIDLE);
	} else {
		Qidle_time = 0;
	}
}

static void pQueIDLE(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doIDLE };
	pushcmd(cmd);
	s.replace(i, endbracket - i + 1, "^!");
}

static bool useTune = false;
static int  tuneTime = 0;

static void pTUNE(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	int number;
	std::string sTime = s.substr(i+6, endbracket - i - 6);
	if (sTime.length() > 0) {
		sscanf(sTime.c_str(), "%d", &number);
		useTune = true;
		tuneTime = number;
	}
	s.replace(i, endbracket - i + 1, "");
}

static bool useWait = false;
static int  waitTime = 0;

static void pWAIT(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	int number;
	std::string sTime = s.substr(i+6, endbracket - i - 6);
	if (sTime.length() > 0) {
		sscanf(sTime.c_str(), "%d", &number);
		useWait = true;
		waitTime = number;
	}
	s.replace(i, endbracket - i + 1, "");
}

static void doneWAIT(void *)
{
	Qwait_time = 0;
	start_tx();
}

static void doWAIT(std::string s)
{
	int number;
	std::string sTime = s.substr(7, s.length() - 8);
	if (sTime.length() > 0) {
		sscanf(sTime.c_str(), "%d", &number);
		Qwait_time = number;
		Fl::add_timeout (number * 1.0, doneWAIT);
	} else
		Qwait_time = 0;
	que_ok = true;
}

static void pQueWAIT(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doWAIT };
	pushcmd(cmd);
	s.replace(i, endbracket - i + 1, "^!");
}


static void pINFO1(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 7, info1msg );
}

static void pINFO2(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 7, info2msg );
}

static void pCLRRX(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace( i, 7, "" );
	ReceiveText->clear();
}

static void pCLRTX(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace( i, 7, "" );
	queue_reset();
	TransmitText->clear();
}

static void pCALL(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 6, inpCall->value() );
}

static void pGET(std::string &s, size_t &i, size_t endbracket)
{
	s.erase( i, 9 );
	GET = true;
}

static void pFREQ(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 6, inpFreq->value() );
}

static void pLOC(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 5, inpLoc->value() );
}

static void pMODE(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 6, active_modem->get_mode_name());
}

static void pNAME(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 6, inpName->value() );
}

static void pQTH(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i,5, inpQth->value() );
}

static void pQSOTIME(std::string &s, size_t &i, size_t endbracket)
{
	qso_time = inpTimeOff->value();
	s.replace( i, 9, qso_time.c_str() );
}

static void pRST(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 5, cut_string(inpRstOut->value()));
}

static void pMYCALL(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 8, inpMyCallsign->value() );
}

static void pMYLOC(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 7, inpMyLocator->value() );
}

static void pMYNAME(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 8, inpMyName->value() );
}

static void pMYQTH(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 7, inpMyQth->value() );
}

static void pMYRST(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 7, inpRstIn->value() );
}

static void pLDT(std::string &s, size_t &i, size_t endbracket)
{
	char szDt[80];
	time_t tmptr;
	tm sTime;
	time (&tmptr);
	localtime_r(&tmptr, &sTime);
	mystrftime(szDt, 79, "%x %H:%M %Z", &sTime);
	s.replace( i, 5, szDt);
}

static void pILDT(std::string &s, size_t &i, size_t endbracket)
{
	char szDt[80];
	time_t tmptr;
	tm sTime;
	time (&tmptr);
	localtime_r(&tmptr, &sTime);
	mystrftime(szDt, 79, "%Y-%m-%d %H:%M%z", &sTime);
	s.replace( i, 6, szDt);
}

static void pZDT(std::string &s, size_t &i, size_t endbracket)
{
	char szDt[80];
	time_t tmptr;
	tm sTime;
	time (&tmptr);
	gmtime_r(&tmptr, &sTime);
	mystrftime(szDt, 79, "%x %H:%MZ", &sTime);
	s.replace( i, 5, szDt);
}

static void pIZDT(std::string &s, size_t &i, size_t endbracket)
{
	char szDt[80];
	time_t tmptr;
	tm sTime;
	time (&tmptr);
	gmtime_r(&tmptr, &sTime);
	mystrftime(szDt, 79, "%Y-%m-%d %H:%MZ", &sTime);
	s.replace( i, 6, szDt);
}

static void pLT(std::string &s, size_t &i, size_t endbracket)
{
	char szDt[80];
	time_t tmptr;
	tm sTime;
	time (&tmptr);
	localtime_r(&tmptr, &sTime);
	mystrftime(szDt, 79, "%H%M", &sTime);
	s.replace( i, 4, szDt);
}

static void pZT(std::string &s, size_t &i, size_t endbracket)
{
	char szDt[80];
	time_t tmptr;
	tm sTime;
	time (&tmptr);
	gmtime_r(&tmptr, &sTime);
	mystrftime(szDt, 79, "%H%MZ", &sTime);
	s.replace( i, 4, szDt);
}

static void pLD(std::string &s, size_t &i, size_t endbracket)
{
	char szDt[80];
	time_t tmptr;
	tm sTime;
	time (&tmptr);
	localtime_r(&tmptr, &sTime);
	mystrftime(szDt, 79, "%Y-%m-%d", &sTime);
	s.replace( i, 4, szDt);
}

static void pZD(std::string &s, size_t &i, size_t endbracket)
{
	char szDt[80];
	time_t tmptr;
	tm sTime;
	time (&tmptr);
	gmtime_r(&tmptr, &sTime);
	mystrftime(szDt, 79, "%Y-%m-%d", &sTime);
	s.replace( i, 4, szDt);
}

static void pID(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	progdefaults.macroid = true;
	s.replace( i, 4, "");
}

static void pTEXT(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	progdefaults.macrotextid = true;
	s.replace( i, 6, "");
}

static void pCWID(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	progdefaults.macroCWid = true;
	s.replace( i, 6, "");
}

static void doDTMF(std::string s)
{
	progdefaults.DTMFstr = s.substr(6, s.length() - 7);
}

static void pDTMF(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	CMDS cmd = {s.substr(i, endbracket - i + 1), doDTMF};
	pushcmd(cmd);
	s.replace(i, endbracket - i + 1, "^!");
}

static void pRX(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace (i, 4, "^r");
}

static void pTX(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.erase(i, 4);
	TransmitON = true;
}

static void pTXRX(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.erase(i, 7);
	ToggleTXRX = true;
}


static void pVER(std::string &s, size_t &i, size_t endbracket)
{
	std::string progname;
	progname = "Fldigi ";
	progname.append(PACKAGE_VERSION);
	s.replace( i, 5, progname );
}

static void pCNTR(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	int  contestval;
	contestval = contest_count.count;
	if (contestval) {
		contest_count.Format(progdefaults.ContestDigits, progdefaults.UseLeadingZeros);
		snprintf(contest_count.szCount, sizeof(contest_count.szCount), contest_count.fmt.c_str(), contestval);
		s.replace (i, 6, cut_string(contest_count.szCount));
	} else
		s.replace (i, 6, "");
}

static void pDECR(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	int  contestval;
	contest_count.count--;
	if (contest_count.count < 0) contest_count.count = 0;
	contestval = contest_count.count;
	s.replace (i, 6, "");
	updateOutSerNo();
}

static void pINCR(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	int  contestval;
	contest_count.count++;
	contestval = contest_count.count;
	s.replace (i, 6, "");
	updateOutSerNo();
}

static void pXIN(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 5, inpXchgIn->value() );
}

static void pXOUT(std::string &s, size_t &i, size_t endbracket)
{
	s.replace( i, 6, cut_string(progdefaults.myXchg.c_str()));
}

static void pXBEG(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace( i, 6, "");
	xbeg = i;
}

static void pXEND(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace( i, 6, "");
	xend = i;
}

static void pSAVEXCHG(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	save_xchg = true;
	s.replace( i, 10, "");
}

static void pLOG(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	qsoSave_cb(0, 0);
	s.replace(i, 5, "");
}

static void pLNW(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace(i, 5, "^L");
}

static void pCLRLOG(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace(i, 10, "^C");
}

static void pMODEM_compSKED(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	size_t	j, k,
			len = s.length();
	std::string name;

	if ((j = s.find('>', i)) == std::string::npos)
		return;
	while (++j < len)
	    if (!isspace(s[j])) break;
	k = j;
	while (++k < len)
	    if (isspace(s[k])  || s[k] == '<') break;
	name = s.substr(j, k - j);
	for (int m = 0; m < NUM_MODES; m++) {
		if (name == mode_info[m].sname) {
			if (active_modem->get_mode() != mode_info[m].mode)
				init_modem(mode_info[m].mode);
			break;
		}
	}
	s.erase(i, k-i);
}

#include <float.h>
#include "re.h"

static void doMODEM(std::string s)
{
	static fre_t re("<!MODEM:([[:alnum:]-]+)((:[[:digit:].+-]*)*)>", REG_EXTENDED);
	std::string tomatch = s;

	if (!re.match(tomatch.c_str())) {
		que_ok = true;
		return;
	}

	const std::vector<regmatch_t>& o = re.suboff();
	std::string name = tomatch.substr(o[1].rm_so, o[1].rm_eo - o[1].rm_so);
	trx_mode m;
	for (m = 0; m < NUM_MODES; m++)
		if (name == mode_info[m].sname)
			break;
	// do we have arguments and a valid modem?
	if (o.size() == 2 || m == NUM_MODES) {
		que_ok = true;
		return;
	}

	// parse arguments
	vector<double> args;
	args.reserve(8);
	char* end;
	double d;
	for (const char* p = s.c_str() + o[2].rm_so + 1; *p; p++) {
		errno = 0;
		d = strtod(p, &end);
		if (!errno && p != end) {
			args.push_back(d);
			p = end;
		}
		else // push an invalid value
			args.push_back(DBL_MIN);
	}

	try {
		switch (m) {
		case MODE_RTTY: // carrier shift, baud rate, bits per char
			if (args.at(0) != DBL_MIN)
				set_rtty_shift((int)args[0]);
			if (args.at(1) != DBL_MIN)
				set_rtty_baud((float)args[1]);
			if (args.at(2) != DBL_MIN)
				set_rtty_bits((int)args[2]);
			if (args.at(3) != DBL_MIN)
				set_rtty_bw((float)args[3]);
			break;
		case MODE_CONTESTIA: // bandwidth, tones
			if (args.at(0) != DBL_MIN)
				set_contestia_bw((int)args[0]);
			if (args.at(1) != DBL_MIN)
				set_contestia_tones((int)args[1]);
			break;
		case MODE_OLIVIA: // bandwidth, tones
			if (args.at(0) != DBL_MIN)
				set_olivia_bw((int)args[0]);
			if (args.at(1) != DBL_MIN)
				set_olivia_tones((int)args[1]);
			break;
		default:
			break;
		}
	}
	catch (const exception& e) { }

	if (active_modem->get_mode() != mode_info[m].mode)
		init_modem_sync(mode_info[m].mode);
	que_ok = true;
}

static void pQueMODEM(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doMODEM };
	pushcmd(cmd);
	s.replace(i, endbracket - i + 1, "^!");
}

static void pMODEM(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	static fre_t re("<MODEM:([[:alnum:]-]+)((:[[:digit:].+-]*)*)>", REG_EXTENDED);

	if (!re.match(s.c_str() + i)) {
		size_t end = s.find('>', i);
		if (end != std::string::npos)
			s.erase(i, end - i);
		return;
	}

	const std::vector<regmatch_t>& o = re.suboff();
	std::string name = s.substr(o[1].rm_so, o[1].rm_eo - o[1].rm_so);
	trx_mode m;
	for (m = 0; m < NUM_MODES; m++)
		if (name == mode_info[m].sname)
			break;
	// do we have arguments and a valid modem?
	if (o.size() == 2 || m == NUM_MODES) {
		if (m < NUM_MODES && active_modem->get_mode() != mode_info[m].mode)
			init_modem(mode_info[m].mode);
		s.erase(i, o[0].rm_eo - i);
		return;
	}

	// parse arguments
	vector<double> args;
	args.reserve(8);
	char* end;
	double d;
	for (const char* p = s.c_str() + o[2].rm_so + 1; *p; p++) {
		errno = 0;
		d = strtod(p, &end);
		if (!errno && p != end) {
			args.push_back(d);
			p = end;
		}
		else // push an invalid value
			args.push_back(DBL_MIN);
	}

	try {
		switch (m) {
		case MODE_RTTY: // carrier shift, baud rate, bits per char
			if (args.at(0) != DBL_MIN)
				set_rtty_shift((int)args[0]);
			if (args.at(1) != DBL_MIN)
				set_rtty_baud((float)args[1]);
			if (args.at(2) != DBL_MIN)
				set_rtty_bits((int)args[2]);
			if (args.at(3) != DBL_MIN)
				set_rtty_bw((float)args[3]);
			break;
		case MODE_CONTESTIA: // bandwidth, tones
			if (args.at(0) != DBL_MIN)
				set_contestia_bw((int)args[0]);
			if (args.at(1) != DBL_MIN)
				set_contestia_tones((int)args[1]);
			break;
		case MODE_OLIVIA: // bandwidth, tones
			if (args.at(0) != DBL_MIN)
				set_olivia_bw((int)args[0]);
			if (args.at(1) != DBL_MIN)
				set_olivia_tones((int)args[1]);
			break;
		default:
			break;
		}
	}
	catch (const exception& e) { }

	if (active_modem->get_mode() != mode_info[m].mode) {
		init_modem(mode_info[m].mode);
		int count = 100;
		while ((active_modem->get_mode() != mode_info[m].mode) && --count)
			MilliSleep(10);
	}

	s.replace(i, endbracket - i + 1, "");
}

static void pAFC(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
  std::string sVal = s.substr(i+5, endbracket - i - 5);
  if (sVal.length() > 0) {
// sVal = on|off|t   [ON, OFF or Toggle]
    if (sVal.compare(0,2,"on") == 0)
      btnAFC->value(1);
    else if (sVal.compare(0,3,"off") == 0)
      btnAFC->value(0);
    else if (sVal.compare(0,1,"t") == 0)
      btnAFC->value(!btnAFC->value());

    btnAFC->do_callback();
  }
  s.replace(i, endbracket - i + 1, "");
}

static void pREV(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
  std::string sVal = s.substr(i+5, endbracket - i - 5);
  if (sVal.length() > 0) {
// sVal = on|off|t   [ON, OFF or Toggle]
    if (sVal.compare(0,2,"on") == 0)
      wf->btnRev->value(1);
    else if (sVal.compare(0,3,"off") == 0)
      wf->btnRev->value(0);
    else if (sVal.compare(0,1,"t") == 0)
      wf->btnRev->value(!wf->btnRev->value());

    wf->btnRev->do_callback();
  }
  s.replace(i, endbracket - i + 1, "");
}

static void pLOCK(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
  std::string sVal = s.substr(i+6, endbracket - i - 6);
  if (sVal.length() > 0) {
// sVal = on|off|t   [ON, OFF or Toggle]
    if (sVal.compare(0,2,"on") == 0)
      wf->xmtlock->value(1);
    else if (sVal.compare(0,3,"off") == 0)
      wf->xmtlock->value(0);
    else if (sVal.compare(0,1,"t") == 0)
      wf->xmtlock->value(!wf->xmtlock->value());

    wf->xmtlock->damage();
    wf->xmtlock->do_callback();
  }
  s.replace(i, endbracket - i + 1, "");
}

static void pTX_RSID(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
  std::string sVal = s.substr(i+8, endbracket - i - 8);
  if (sVal.length() > 0) {
    // sVal = on|off|t   [ON, OFF or Toggle]
    if (sVal.compare(0,2,"on") == 0)
      btnTxRSID->value(1);
    else if (sVal.compare(0,3,"off") == 0)
      btnTxRSID->value(0);
    else if (sVal.compare(0,1,"t") == 0)
      btnTxRSID->value(!btnTxRSID->value());

    btnTxRSID->do_callback();
  }
  s.replace(i, endbracket - i + 1, "");
}

static void pRX_RSID(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
  std::string sVal = s.substr(i+8, endbracket - i - 8);
  if (sVal.length() > 0) {
    // sVal = on|off|t   [ON, OFF or Toggle]
    if (sVal.compare(0,2,"on") == 0)
      btnRSID->value(1);
    else if (sVal.compare(0,3,"off") == 0)
      btnRSID->value(0);
    else if (sVal.compare(0,1,"t") == 0)
      btnRSID->value(!btnRSID->value());

    btnRSID->do_callback();
  }
  s.replace(i, endbracket - i + 1, "");
}

#ifdef __WIN32__
static void pTALK(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
  std::string sVal = s.substr(i+6, endbracket - i - 6);
  if (sVal.length() > 0) {
    // sVal = on|off   [ON, OFF]
    if (sVal.compare(0,2,"on") == 0)
		open_talker();
    else if (sVal.compare(0,3,"off") == 0)
		close_talker();
    else if (sVal.compare(0,1,"t") == 0)
		toggle_talker();
  }
  s.replace(i, endbracket - i + 1, "");
}
#endif

static void pSRCHUP(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace( i, 8, "");
	active_modem->searchUp();
	if (progdefaults.WaterfallClickInsert)
	        wf->insert_text(true);
}

static void pSRCHDN(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace( i, 8, "");
	active_modem->searchDown();
	if (progdefaults.WaterfallClickInsert)
	         wf->insert_text(true);
}

static void pGOHOME(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace( i, 8, "");
	if (active_modem == cw_modem)
		active_modem->set_freq(progdefaults.CWsweetspot);
	else if (active_modem == rtty_modem)
		active_modem->set_freq(progdefaults.RTTYsweetspot);
	else
		active_modem->set_freq(progdefaults.PSKsweetspot);
}

static void doGOHOME(std::string s)
{
	if (active_modem == cw_modem)
		active_modem->set_freq(progdefaults.CWsweetspot);
	else if (active_modem == rtty_modem)
		active_modem->set_freq(progdefaults.RTTYsweetspot);
	else
		active_modem->set_freq(progdefaults.PSKsweetspot);
	que_ok = true;
}

static void pQueGOHOME(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doGOHOME };
	pushcmd(cmd);
	s.replace(i, endbracket - i + 1, "^!");
}

static void pGOFREQ(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	int number;
	std::string sGoFreq = s.substr(i+8, endbracket - i - 8);
	if (sGoFreq.length() > 0) {
		sscanf(sGoFreq.c_str(), "%d", &number);
		if (number < progdefaults.LowFreqCutoff)
			number = progdefaults.LowFreqCutoff;
		if (number > progdefaults.HighFreqCutoff)
			number = progdefaults.HighFreqCutoff;
		active_modem->set_freq(number);
	}
	s.replace(i, endbracket - i + 1, "");
}

static void doGOFREQ(std::string s)
{
	int number;
	std::string sGoFreq = s.substr(9, s.length() - 10);
	if (sGoFreq.length() > 0) {
		sscanf(sGoFreq.c_str(), "%d", &number);
		if (number < progdefaults.LowFreqCutoff)
			number = progdefaults.LowFreqCutoff;
		if (number > progdefaults.HighFreqCutoff)
			number = progdefaults.HighFreqCutoff;
		active_modem->set_freq(number);
	}
	que_ok = true;
}

static void pQueGOFREQ(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doGOFREQ };
	pushcmd(cmd);
	s.replace(i, endbracket - i + 1, "^!");
}

static void pQSYTO(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace( i, 7, "");
	do_qsy(true);
}

static void pQSYFM(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.replace( i, 7, "");
	do_qsy(false);
}

static void pQSY(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	int rf = 0;
	int audio = 0;
	float rfd = 0;
	std::string sGoFreq = s.substr(i+5, endbracket - i - 5);
	// no frequency(s) specified
	if (sGoFreq.length() == 0) {
		s.replace(i, endbracket-i+1, "");
		return;
	}
	// rf first value
	sscanf(sGoFreq.c_str(), "%f", &rfd);
	if (rfd > 0)
		rf = (int)(1000*rfd);
	size_t pos;
	if ((pos = sGoFreq.find(":")) != std::string::npos) {
		// af second value
		sGoFreq.erase(0, pos+1);
		if (sGoFreq.length())
			sscanf(sGoFreq.c_str(), "%d", &audio);
		if (audio < 0) audio = 0;
		if (audio < progdefaults.LowFreqCutoff)
			audio = progdefaults.LowFreqCutoff;
		if (audio > progdefaults.HighFreqCutoff)
			audio = progdefaults.HighFreqCutoff;
	}
	if (rf && rf != wf->rfcarrier())
		qsy(rf, audio);
	else
		active_modem->set_freq(audio);

	s.replace(i, endbracket - i + 1, "");
}

static void doQSY(std::string s)
{
	int rf = 0;
	int audio = 0;
	float rfd = 0;
	std::string sGoFreq;
	sGoFreq = s.substr(6, s.length() - 7);
	// no frequency(s) specified
	if (sGoFreq.length() == 0) {
		que_ok = true;
		return;
	}
	// rf first value
	sscanf(sGoFreq.c_str(), "%f", &rfd);
	if (rfd > 0)
		rf = (int)(1000*rfd);
	size_t pos;
	if ((pos = sGoFreq.find(":")) != std::string::npos) {
		// af second value
		sGoFreq.erase(0, pos+1);
		if (sGoFreq.length())
			sscanf(sGoFreq.c_str(), "%d", &audio);
		if (audio < 0) audio = 0;
		if (audio < progdefaults.LowFreqCutoff)
			audio = progdefaults.LowFreqCutoff;
		if (audio > progdefaults.HighFreqCutoff)
			audio = progdefaults.HighFreqCutoff;
	}
	if (rf && rf != wf->rfcarrier())
		qsy(rf, audio);
	else
		active_modem->set_freq(audio);
	que_ok = true;
}

static void pQueQSY(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doQSY };
	pushcmd(cmd);
	s.replace(i, endbracket - i + 1, "^!");
}

static void pRIGMODE(std::string& s, size_t& i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	std::string sMode = s.substr(i+9, endbracket - i - 9);
	qso_opMODE->value(sMode.c_str());
	cb_qso_opMODE();
	s.replace(i, endbracket - i + 1, "");
}

static void pFILWID(std::string& s, size_t& i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	std::string sWidth = s.substr(i+8, endbracket - i - 8);
	qso_opBW->value(sWidth.c_str());
	cb_qso_opBW();
	s.replace(i, endbracket - i + 1, "");
}

void set_macro_env(void)
{
	enum {
#ifndef __WOE32__
	       pSKEDH, FLDIGI_RX_IPC_KEY, FLDIGI_TX_IPC_KEY,
#endif
	       FLDIGI_XMLRPC_ADDRESS, FLDIGI_XMLRPC_PORT,
	       FLDIGI_ARQ_ADDRESS, FLDIGI_ARQ_PORT,

	       FLDIGI_VERSION_ENVVAR, FLDIGI_PID, FLDIGI_CONFIG_DIR,

	       FLDIGI_MY_CALL, FLDIGI_MY_NAME, FLDIGI_MY_LOCATOR,

	       FLDIGI_MODEM, FLDIGI_MODEM_LONG_NAME, FLDIGI_DIAL_FREQUENCY,
	       FLDIGI_AUDIO_FREQUENCY, FLDIGI_FREQUENCY,

	       FLDIGI_LOG_FREQUENCY, FLDIGI_LOG_TIME_ON, FLDIGI_LOG_TIME_OFF, FLDIGI_LOG_CALL, FLDIGI_LOG_NAME,
	       FLDIGI_LOG_RST_IN, FLDIGI_LOG_RST_OUT, FLDIGI_LOG_QTH, FLDIGI_LOG_LOCATOR,
	       FLDIGI_LOG_NOTES, FLDIGI_AZ, ENV_SIZE
	};

	struct {
		const char* var;
		const char* val;
	} env[] = {
#ifndef __WOE32__
		{ "pSKEDH", "" },
		{ "FLDIGI_RX_IPC_KEY", "" },
		{ "FLDIGI_TX_IPC_KEY", "" },
#endif
		{ "FLDIGI_XMLRPC_ADDRESS", progdefaults.xmlrpc_address.c_str() },
		{ "FLDIGI_XMLRPC_PORT", progdefaults.xmlrpc_port.c_str() },
		{ "FLDIGI_ARQ_ADDRESS", progdefaults.arq_address.c_str() },
		{ "FLDIGI_ARQ_PORT", progdefaults.arq_port.c_str() },

		{ "FLDIGI_VERSION", PACKAGE_VERSION },
		{ "FLDIGI_PID", "" },
		{ "FLDIGI_CONFIG_DIR", HomeDir.c_str() },

		{ "FLDIGI_MY_CALL", progdefaults.myCall.c_str() },
		{ "FLDIGI_MY_NAME", progdefaults.myName.c_str() },
		{ "FLDIGI_MY_LOCATOR", progdefaults.myLocator.c_str() },

		{ "FLDIGI_MODEM", mode_info[active_modem->get_mode()].sname },
		{ "FLDIGI_MODEM_LONG_NAME", mode_info[active_modem->get_mode()].name },
		{ "FLDIGI_DIAL_FREQUENCY", "" },
		{ "FLDIGI_AUDIO_FREQUENCY", "" },
		{ "FLDIGI_FREQUENCY", "" },

		// logging frame
		{ "FLDIGI_LOG_FREQUENCY", inpFreq->value() },
		{ "FLDIGI_LOG_TIME_ON", inpTimeOn->value() },
		{ "FLDIGI_LOG_TIME_OFF", inpTimeOff->value() },
		{ "FLDIGI_LOG_CALL", inpCall->value() },
		{ "FLDIGI_LOG_NAME", inpName->value() },
		{ "FLDIGI_LOG_RST_IN", inpRstIn->value() },
		{ "FLDIGI_LOG_RST_OUT", inpRstOut->value() },
		{ "FLDIGI_LOG_QTH", inpQth->value() },
		{ "FLDIGI_LOG_LOCATOR", inpLoc->value() },
		{ "FLDIGI_LOG_NOTES", inpNotes->value() },
		{ "FLDIGI_AZ", inpAZ->value() }
	};

#ifndef __WOE32__
	// pSKEDH
	static std::string pSKEDh = ScriptsDir;
	pSKEDh.erase(pSKEDh.length()-1,1);
	const char* p;
	if ((p = getenv("pSKEDH")))
		pSKEDh.append(":").append(p);
	env[pSKEDH].val = pSKEDh.c_str();

	// IPC keys
        char key[2][8];
	snprintf(key[0], sizeof(key[0]), "%d", progdefaults.rx_msgid);
	env[FLDIGI_RX_IPC_KEY].val = key[0];
	snprintf(key[1], sizeof(key[1]), "%d", progdefaults.tx_msgid);
	env[FLDIGI_TX_IPC_KEY].val = key[1];
#endif

	// pid
	char pid[6];
	snprintf(pid, sizeof(pid), "%d", getpid());
	env[FLDIGI_PID].val = pid;

	// frequencies
	char dial_freq[20];
	snprintf(dial_freq, sizeof(dial_freq), "%lld", wf->rfcarrier());
	env[FLDIGI_DIAL_FREQUENCY].val = dial_freq;
	char audio_freq[6];
	snprintf(audio_freq, sizeof(audio_freq), "%d", active_modem->get_freq());
	env[FLDIGI_AUDIO_FREQUENCY].val = audio_freq;
	char freq[20];
	snprintf(freq, sizeof(freq), "%lld", wf->rfcarrier() + (wf->USB()
								? active_modem->get_freq()
								: -active_modem->get_freq()));
	env[FLDIGI_FREQUENCY].val = freq;

	// debugging vars
#if !defined(NDEBUG) && !defined(__WOE32__)
	unsetenv("FLDIGI_NO_EXEC");
	unsetenv("MALLOC_CHECK_");
	unsetenv("MALLOC_PERTURB_");
#endif

	for (size_t j = 0; j < ENV_SIZE; j++)
		setenv(env[j].var, env[j].val, 1);

	string path = getenv("PATH");
	string mypath = ScriptsDir;
	if (mypath[mypath.length()-1] == '/')
		mypath.erase(mypath.length()-1, 1);
	mypath.append(":");
	path.insert(0,mypath);
	setenv("PATH", path.c_str(), 1);
}

// this is only for the case where the user tries to nest <EXEC>...
// as in
// <EXEC> ... <EXEC> ... </EXEC></EXEC>
// which is not permitted
static void pEND_EXEC(std::string &s, size_t &i, size_t endbracket)
{
	s.replace(i, endbracket - i + 1, "");
	return;
}

#ifndef __MINGW32__
static void pEXEC(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}

	size_t start, end;
	if ((start = s.find('>', i)) == std::string::npos ||
		(end = s.rfind("</EXEC>")) == std::string::npos) {
		i++;
		return;
	}
	start++;
	i++;

	std::string execstr = s.substr(start, end-start);
	within_exec = true;
	MACROTEXT m;
	execstr = m.expandMacro(execstr);
//	execstr.insert(0,ScriptsDir);
	within_exec = false;

	int pfd[2];
	if (pipe(pfd) == -1) {
		LOG_PERROR("pipe");
		return;
	}
	pid_t pid;
	switch (pid = fork()) {
	case -1:
		LOG_PERROR("fork");
		return;
	case 0: // child
		close(pfd[0]);
		if (dup2(pfd[1], STDOUT_FILENO) != STDOUT_FILENO) {
			LOG_PERROR("dup2");
			exit(EXIT_FAILURE);
		}
		close(pfd[1]);
		set_macro_env();
		execl("/bin/sh", "sh", "-c", execstr.c_str(), (char *)NULL);
		perror("execl");
		exit(EXIT_FAILURE);
	}
	// parent
	close(pfd[1]);
	FILE* fp = fdopen(pfd[0], "r");
	if (!fp) {
		LOG_PERROR("fdopen");
		close(pfd[0]);
		return;
	}

	start = --i;
	end = s.find('>', end) + 1;
	s.erase(start, end-start);
	char ln[BUFSIZ];
	while (fgets(ln, sizeof(ln), fp)) {
		end = strlen(ln);
		s.insert(start, ln, end);
		start += end;
	}
	// delete the trailing newline of what we read
	if (start > i && s[start - 1] == '\n')
		s.erase(start - 1, 1);

	fclose(fp);
	close(pfd[0]);

	// what should we do with the shell-generated text?
	// option 1: uncomment this line to skip & ignore it
	// i = start;
	// option 2: do nothing and allow it to be parsed for more macros
}
#else // !__MINGW32__

static void pEXEC(std::string& s, size_t& i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	size_t start, end;
	if ((start = s.find('>', i)) == std::string::npos ||
		(end = s.rfind("</EXEC>")) == std::string::npos) {
		i++;
		return;
	}
	start++;

	std::string execstr = s.substr(start, end-start);
	within_exec = true;
	MACROTEXT m;
	execstr = m.expandMacro(execstr);
	within_exec = false;

	char* cmd = strdup(execstr.c_str());

	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	memset(&si, 0, sizeof(si));
	si.cb = sizeof(si);
	memset(&pi, 0, sizeof(pi));
	if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
		LOG_ERROR("CreateProcess failed with error code %ld", GetLastError());
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	free(cmd);

	s.erase(i, end + strlen("</EXEC>") - i);
}
#endif // !__MINGW32__

static void pEQSL(std::string& s, size_t& i, size_t endbracket)
{
	if (within_exec || progdefaults.eqsl_when_logged) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	size_t start = s.find(':', i);

	std::string msg = "";
	if (start != std::string::npos)
		msg = s.substr(start + 1, endbracket-start-1);

	makeEQSL(msg.c_str());

	s.replace(i, endbracket - i + 1, "");
	return;
}

static void MAPIT(int how)
{
	float lat = 0, lon = 0;
	std::string sCALL = inpCall->value();
	std::string sLOC = inpLoc->value();

	std::string url = "http://maps.google.com/maps?q=";

	if (how > 1 && !lookup_country.empty()) {
		url.append(lookup_addr1).append(",").append(lookup_addr2).append(",");
		url.append(lookup_state).append(",").append(lookup_country);
	} else {
		if (how > 0 && (!lookup_latd.empty() && !lookup_lond.empty())) {
			url.append(lookup_latd).append(",");
			url.append(lookup_lond);
		} else {
			if (sLOC.empty()) return;
			if (sLOC.length() < 4) return;
			if (sLOC.length() < 6) sLOC.append("aa");
			for (size_t i = 0; i < 6; i++) sLOC[i] = toupper(sLOC[i]);
			if (sLOC[0] -'A' > 17 || sLOC[4] - 'A' > 23 ||
				sLOC[1] -'A' > 17 || sLOC[5] - 'A' > 23 ||
				!isdigit(sLOC[2]) || !isdigit(sLOC[3])) return;
			lon =	-180.0 + 
					(sLOC[0] - 'A') * 20 +
					(sLOC[2] - '0') * 2 +
					(sLOC[4] - 'A' + 0.5) / 12;
			lat = -90.0 + 
					(sLOC[1] - 'A') * 10 +
					(sLOC[3] - '0') +
					(sLOC[5] - 'A' + 0.5) / 24;
			char sdata[20];
			snprintf(sdata, sizeof(sdata),"%10.6f", lat);
			url.append(sdata).append(",");
			snprintf(sdata, sizeof(sdata),"%10.6f", lon);
			url.append(sdata);
		}
	}
	if (!sCALL.empty()) url.append("(").append(sCALL).append(")");
	else url.append("(nocall)");
	url.append("&t=p&z=10");
	cb_mnuVisitURL(NULL, (void*)url.c_str());
}

static void pMAPIT(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	std::string sVal = s.substr(i + 7, endbracket - i - 7);
	if (sVal.length() > 0) {
		if (sVal.compare(0,3,"adr") == 0)
			REQ(MAPIT,2);
		else if (sVal.compare(0,6,"latlon") == 0)
			REQ(MAPIT,1);
		else if (sVal.compare(0,3,"loc") == 0)
			REQ(MAPIT,0);
		else
			REQ(MAPIT,2);
	} else
		REQ(MAPIT,2);
	s.erase(i, s.find('>', i) + 1 - i);
	expand = false;
}

static void pSTOP(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.erase(i, s.find('>', i) + 1 - i);
	expand = false;
}

static void pCONT(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	s.erase(i, s.find('>', i) + 1 - i);
	expand = true;
}

static void pSKED(std::string &s, size_t &i, size_t endbracket)
{
	if (within_exec) {
		s.replace(i, endbracket - i + 1, "");
		return;
	}
	std::string data = s.substr(i+6, endbracket - i - 6);
	size_t p = data.find(":");
	if (p == std::string::npos) {
		exec_date = zdate();
		exec_time = data;
		if (exec_time.empty()) exec_time = ztime();
	} else {
		exec_time = data.substr(0, p);
		exec_date = data.substr(p+1);
	}
	timed_exec = true;
	s.replace(i, endbracket - i + 1, "");
}

void queue_reset()
{
	if (!cmds.empty()) {
		Fl::remove_timeout(post_queue_execute);
		Fl::remove_timeout(queue_execute_after_rx);
		Fl::remove_timeout(doneIDLE);
		Fl::remove_timeout(doneWAIT);
		while (!cmds.empty()) cmds.pop();
	}
	Qwait_time = 0;
	Qidle_time = 0;
	que_ok = true;
}

static void postQueue(std::string s)
{
	ReceiveText->add(s.c_str(), FTextBase::CTRL);
}

void queue_execute()
{
	if (cmds.empty()) {
		Qwait_time = 0;
		Qidle_time = 0;
		que_ok = true;
		return;
	}
	CMDS cmd = cmds.front();
	cmds.pop();
	cmd.fp(cmd.cmd);
	LOG_INFO("%s", cmd.cmd.c_str());
	REQ(postQueue, cmd.cmd.append("\n"));
	return;
}

bool queue_must_rx()
{
static std::string rxcmds = "<!MOD<!WAI<!GOH<!QSY<!GOF";
	if (cmds.empty()) return false;
	CMDS cmd = cmds.front();
	return (rxcmds.find(cmd.cmd.substr(0,5)) != std::string::npos);
}

struct MTAGS { const char *mTAG; void (*fp)(std::string &, size_t&, size_t );};

static const MTAGS mtags[] = {
{"<CALL>",		pCALL},
{"<FREQ>",		pFREQ},
{"<LOC>",		pLOC},
{"<MODE>",		pMODE},
{"<NAME>",		pNAME},
{"<QTH>",		pQTH},
{"<RST>",		pRST},
{"<MYCALL>",	pMYCALL},
{"<MYLOC>",		pMYLOC},
{"<MYNAME>",	pMYNAME},
{"<MYQTH>",		pMYQTH},
{"<MYRST>",		pMYRST},
{"<QSOTIME>",	pQSOTIME},
{"<INFO1>",		pINFO1},
{"<INFO2>",		pINFO2},
{"<LDT>",		pLDT},
{"<ILDT>",		pILDT},
{"<ZDT>",		pZDT},
{"<IZDT>",		pIZDT},
{"<LT>",		pLT},
{"<ZT>",		pZT},
{"<LD>",		pLD},
{"<ZD>",		pZD},
{"<ID>",		pID},
{"<TEXT>",		pTEXT},
{"<CWID>",		pCWID},
{"<RX>",		pRX},
{"<TX>",		pTX},
{"<TX/RX>",		pTXRX},
{"<VER>",		pVER},
{"<CNTR>",		pCNTR},
{"<DECR>",		pDECR},
{"<INCR>",		pINCR},
{"<X1>",		pXOUT},
{"<XIN>",		pXIN},
{"<XOUT>",		pXOUT},
{"<XBEG>",		pXBEG},
{"<XEND>",		pXEND},
{"<SAVEXCHG>",	pSAVEXCHG},
{"<LOG>",		pLOG},
{"<LNW>",		pLNW},
{"<CLRLOG>",	pCLRLOG},
{"<EQSL",		pEQSL},
{"<TIMER:",		pTIMER},
{"<IDLE:",		pIDLE},
{"<TUNE:",		pTUNE},
{"<WAIT:",		pWAIT},
{"<MODEM>",		pMODEM_compSKED},
{"<MODEM:",		pMODEM},
{"<EXEC>",		pEXEC},
{"</EXEC>",		pEND_EXEC},
{"<STOP>",		pSTOP},
{"<CONT>",		pCONT},
{"<GET>",		pGET},
{"<CLRRX>",		pCLRRX},
{"<CLRTX>",		pCLRTX},
{"<FILE:",		pFILE},
{"<WPM:",		pWPM},
{"<RISE:",		pRISETIME},
{"<PRE:",		pPRE},
{"<POST:",		pPOST},
{"<AFC:",		pAFC},
{"<LOCK:",		pLOCK},
{"<REV:",		pREV},
{"<RXRSID:",	pRX_RSID},
{"<TXRSID:",	pTX_RSID},
{"<DTMF:",		pDTMF},
{"<SRCHUP>",	pSRCHUP},
{"<SRCHDN>",	pSRCHDN},
{"<GOHOME>",	pGOHOME},
{"<GOFREQ:",	pGOFREQ},
{"<QSY:",		pQSY},
{"<QSYTO>",		pQSYTO},
{"<QSYFM>",		pQSYFM},
{"<RIGMODE:",	pRIGMODE},
{"<FILWID:",	pFILWID},
{"<MAPIT:",		pMAPIT},
{"<MAPIT>",		pMAPIT},
{"<REPEAT>",	pREPEAT},
{"<SKED:",		pSKED},
#ifdef __WIN32__
{"<TALK:",		pTALK},
#endif
{"<!WPM:",		pQueWPM},
{"<!RISE:",		pQueRISETIME},
{"<!PRE:",		pQuePRE},
{"<!POST:",		pQuePOST},
{"<!GOHOME>",	pQueGOHOME},
{"<!GOFREQ:",	pQueGOFREQ},
{"<!QSY:",		pQueQSY},
{"<!IDLE:",		pQueIDLE},
{"<!WAIT:",		pQueWAIT},
{"<!MODEM:",	pQueMODEM},
{0, 0}
};

int MACROTEXT::loadMacros(const std::string& filename)
{
	std::string mLine;
	std::string mName;
	std::string mDef;
	bool   inMacro = false;
	int    mNumber = 0;
	unsigned long int	   crlf; // 64 bit cpu's
	char   szLine[4096];
	bool   convert = false;

	ifstream mFile(filename.c_str());

	if (!mFile) {
		create_new_macros();
	} else {

	mFile.getline(szLine, 4095);
	mLine = szLine;
	if (mLine.find("//fldigi macro definition file") != 0) {
		mFile.close();
		return -2;
	}
	if (mLine.find("extended") == std::string::npos) {
		convert = true;
		changed = true;
	}
// clear all of the macros
	for (int i = 0; i < MAXMACROS; i++) {
		name[i] = "";
		text[i] = "";
	}
	inMacro = false;
	while (!mFile.eof()) {
		mFile.getline(szLine,4095);
		mLine = szLine;
		if (mLine.find("//") == 0) // skip over all comment lines
			continue;
		if (mLine.find("/$") == 0) {
			int idx = mLine.find_first_not_of("0123456789", 3);
			sscanf((mLine.substr(3, idx - 3)).c_str(), "%d", &mNumber);
			if (mNumber < 0 || mNumber > (MAXMACROS - 1))
				break;
			if (convert && mNumber > 9) mNumber += 2;
            name[mNumber] = mLine.substr(idx+1);
			continue;
		}
		while ((crlf = mLine.find("\\n")) != std::string::npos) {
			mLine.erase(crlf, 2);
			mLine.append("\n");
		}
		text[mNumber] = text[mNumber] + mLine;
	}
	mFile.close();
}

	return 0;
}

void MACROTEXT::loadDefault()
{
	int erc;
	std::string Filename = MacrosDir;
	if (progdefaults.UseLastMacro == true)
		Filename.append(progStatus.LastMacroFile);
	else {
		Filename.append("macros.mdf");
		progStatus.LastMacroFile = "macros.mdf";
	}
	if ((erc = loadMacros(Filename)) != 0)
#ifndef __WOE32__
		LOG_ERROR("Error #%d loading %s\n", erc, Filename.c_str());
#else
	;
#endif
}

void MACROTEXT::openMacroFile()
{
	std::string deffilename = MacrosDir;
	deffilename.append(progStatus.LastMacroFile);
    const char *p = FSEL::select(_("Open macro file"), _("Fldigi macro definition file\t*.mdf"), deffilename.c_str());
    if (p) {
		loadMacros(p);
		progStatus.LastMacroFile = fl_filename_name(p);
	}
	showMacroSet();
}

void MACROTEXT::saveMacroFile()
{
	std::string deffilename = MacrosDir;
	deffilename.append(progStatus.LastMacroFile);
    const char *p = FSEL::saveas(_("Save macro file"), _("Fldigi macro definition file\t*.mdf"), deffilename.c_str());
    if (p) {
		saveMacros(p);
		progStatus.LastMacroFile = fl_filename_name(p);
	}
}

void MACROTEXT::loadnewMACROS(std::string &s, size_t &i, size_t endbracket)
{
	std::string fname = s.substr(i+8, endbracket - i - 8);
	if (fname.length() > 0) {
		loadMacros(fname);
		progStatus.LastMacroFile = fl_filename_name(fname.c_str());
	}
	s.replace(i, endbracket - i + 1, "");
	showMacroSet();
}

std::string MACROTEXT::expandMacro(std::string &s)
{
	size_t idx = 0;
	expand = true;
	TransmitON = false;
	ToggleTXRX = false;
//	mNbr = n;
	expanded = s;//text[n];
	const MTAGS *pMtags;

	xbeg = xend = -1;
	save_xchg = false;
	progStatus.repeatMacro = -1;
	text2repeat.clear();
	idleTime = 0;
	waitTime = 0;
	tuneTime = 0;

	while ((idx = expanded.find('<', idx)) != std::string::npos) {
		size_t endbracket = expanded.find('>',idx);
 		if (expanded.find("<MACROS:",idx) == idx) {
			loadnewMACROS(expanded, idx, endbracket);
			idx++;
			continue;
		}
		// we must handle this specially
		if (expanded.find("<CONT>", idx) == idx)
			pCONT(expanded, idx, endbracket);
		if (!expand) {
			idx++;
			continue;
		}

		pMtags = mtags;
		while (pMtags->mTAG != 0) {
			if (expanded.find(pMtags->mTAG,idx) == idx) {
				pMtags->fp(expanded,idx, endbracket);
				break;
			}
			pMtags++;
		}
		if (pMtags->mTAG == 0)
			idx++;
	}
	if (GET) {
		size_t pos1 = expanded.find("$NAME");
		size_t pos2 = expanded.find("$QTH");
		size_t pos3 = expanded.find("$LOC");
		if (pos1 != std::string::npos && pos2 != std::string::npos) {
			pos1 += 5;
			inpName->value(expanded.substr(pos1, pos2 - pos1).c_str());
		}
		if (pos2 != std::string::npos) {
			pos2 += 4;
			inpQth->value(expanded.substr(pos2, pos3 - pos2).c_str());
		}
		if (pos3 != std::string::npos) {
			pos3 += 4;
			inpLoc->value(expanded.substr(pos3).c_str());
		}
		GET = false;
		return "";
	}

	if (xbeg != std::string::npos && xend != std::string::npos && xend > xbeg) {
		qso_exchange = expanded.substr(xbeg, xend - xbeg);
	} else if (save_xchg) {
		qso_exchange = expanded;
		save_xchg = false;
	}

// force "^r" to be last tag in the expanded std::string
	if ((idx = expanded.find("^r")) != std::string::npos) {
		expanded.erase(idx, 2);
		expanded.append("^r");
	}

	return expanded;
}

void idleTimer(void *)
{
	macro_idle_on = false;
}

static void continueMacro(void *)
{
	if ( TransmitON ) {
		active_modem->set_stopflag(false);
		if (macro_idle_on && idleTime > 0)
			Fl::add_timeout(idleTime, idleTimer);
		start_tx();
		TransmitON = false;
	}
	text2send.clear();
}

static void finishTune(void *)
{
	trx_receive();
// delay to allow tx/rx loop to change state
	Fl::add_timeout(0.5, continueMacro);
}

static void finishWait(void *)
{
	if (useTune && tuneTime > 0) {
		trx_tune();
		Fl::add_timeout(tuneTime, finishTune);
		useTune = false;
		return;
	}
	if ( TransmitON ) {
		active_modem->set_stopflag(false);
		if (macro_idle_on && idleTime > 0)
			Fl::add_timeout(idleTime, idleTimer);
		start_tx();
		TransmitON = false;
	}
}

static void set_button(Fl_Button* button, bool value)
{
	button->value(value);
	button->do_callback();
}

void MACROTEXT::timed_execute()
{
	queue_reset();
	TransmitText->clear();
	text2send = expandMacro(exec_string);
	TransmitText->add(text2send.c_str());
	exec_string.clear();
	active_modem->set_stopflag(false);
	start_tx();
}

void MACROTEXT::execute(int n)
{
	mNbr = n;
	text2send = expandMacro(text[n]);

	if (timed_exec) {
		progStatus.repeatMacro = -1;
		exec_string = text[n];
		timed_exec = false;
		startTimedExecute(name[n]);
		return;
	}

	if (progStatus.repeatMacro == -1)
		TransmitText->add( text2send.c_str() );
	else {
		size_t p = std::string::npos;
		text2send = text[n];
		while ((p = text2send.find('<')) != std::string::npos)
			text2send[p] = '[';
		while ((p = text2send.find('>')) != std::string::npos)
			text2send[p] = ']';
		TransmitText->add( text2send.c_str() );
	}
	text2send.clear();

	if (ToggleTXRX) {
		text2send.clear();
		if (!wf->xmtrcv->value()) {
			REQ(set_button, wf->xmtrcv, true);
			if (macro_idle_on && idleTime > 0)
				Fl::add_timeout(idleTime, idleTimer);
		} else
			REQ(set_button, wf->xmtrcv, false);
		return;
	}
	if (useWait && waitTime > 0) {
		Fl::add_timeout(waitTime, finishWait);
		useWait = false;
		return;
	}
	if (useTune && tuneTime > 0) {
		trx_tune();
		Fl::add_timeout(tuneTime, finishTune);
		useTune = false;
		return;
	}
	if ( TransmitON ) {
		if (macro_idle_on && idleTime > 0)
			Fl::add_timeout(idleTime, idleTimer);

		active_modem->set_stopflag(false);
		start_tx();
		TransmitON = false;
	}
}

void MACROTEXT::repeat(int n)
{
	expandMacro(text[n]);
	LOG_WARN("%s",text2repeat.c_str());
	macro_idle_on = false;
	if (idleTime) progStatus.repeatIdleTime = idleTime;
}

MACROTEXT::MACROTEXT()
{
	changed = false;
	char szname[5];
	for (int i = 0; i < MAXMACROS; i++) {
		snprintf(szname, sizeof(szname), "F-%d", i+1);
		name[i] = szname;//"";
		text[i] = "";
	}
}


static std::string mtext =
"//fldigi macro definition file extended\n\
// This file defines the macro structure(s) for the digital modem program, fldigi\n\
// It also serves as a basis for any macros that are written by the user\n\
//\n\
// The top line of this file should always be the first line in every macro \n\
// definition file (.mdf) for the fldigi program to recognize it as such.\n\
//\n\
";

void MACROTEXT::saveMacros(const std::string& fname) {
	std::string work;
	ofstream mfile(fname.c_str());
	mfile << mtext;
	for (int i = 0; i < MAXMACROS; i++) {
		mfile << "\n//\n// Macro # " << i+1 << "\n";
		mfile << "/$ " << i << " " << macros.name[i].c_str() << "\n";
		work = macros.text[i];
		size_t pos;
		pos = work.find('\n');
		while (pos != std::string::npos) {
			work.insert(pos, "\\n");
			pos = work.find('\n', pos + 3);
		}
		mfile << work.c_str();
	}
	mfile << "\n";
	mfile.close();
	changed = false;
}

