1 // ----------------------------------------------------------------------------
2 // macros.cxx
3 //
4 // Copyright (C) 2007-2010
5 //		Dave Freese, W1HKJ
6 // Copyright (C) 2008-2010
7 //		Stelios Bounanos, M0GLD
8 // Copyright (C) 2009
9 //		Chris Sylvain, KB3CS
10 //
11 // This file is part of fldigi.
12 //
13 // Fldigi is free software: you can redistribute it and/or modify
14 // it under the terms of the GNU General Public License as published by
15 // the Free Software Foundation, either version 3 of the License, or
16 // (at your option) any later version.
17 //
18 // Fldigi is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 // GNU General Public License for more details.
22 //
23 // You should have received a copy of the GNU General Public License
24 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
25 // ----------------------------------------------------------------------------
26 
27 #include <config.h>
28 #include <sys/time.h>
29 #include <ctime>
30 
31 #include "macros.h"
32 
33 #include "gettext.h"
34 #include "main.h"
35 #include "misc.h"
36 
37 #include "fl_digi.h"
38 #include "timeops.h"
39 #include "configuration.h"
40 #include "confdialog.h"
41 #include "logger.h"
42 #include "newinstall.h"
43 #include "globals.h"
44 #include "debug.h"
45 #include "status.h"
46 #include "trx.h"
47 #include "modem.h"
48 #include "qrunner.h"
49 #include "waterfall.h"
50 #include "rigsupport.h"
51 #include "network.h"
52 #include "logsupport.h"
53 #include "icons.h"
54 #include "weather.h"
55 #include "utf8file_io.h"
56 #include "xmlrpc.h"
57 #include "rigio.h"
58 #include "strutil.h"
59 #include "threads.h"
60 
61 #include <FL/Fl.H>
62 #include <FL/filename.H>
63 #include "fileselect.h"
64 
65 #include <ctime>
66 #include <cstdio>
67 #include <cstdlib>
68 #include <unistd.h>
69 #include <string>
70 #include <sstream>
71 #include <fstream>
72 #include <queue>
73 #include <stack>
74 
75 #ifdef __WIN32__
76 #include "speak.h"
77 #endif
78 
79 #include "audio_alert.h"
80 
81 #include <float.h>
82 #include "re.h"
83 
84 //using namespace std;
85 
86 static pthread_mutex_t	exec_mutex = PTHREAD_MUTEX_INITIALIZER;
87 
88 struct CMDS { std::string cmd; void (*fp)(std::string); };
89 static queue<CMDS> Tx_cmds;
90 static queue<CMDS> Rx_cmds;
91 
92 bool txque_wait = false;
93 
94 static std::string buffered_macro;
95 static bool buffered = false;
96 static size_t buffered_pointer;
97 
98 /*
99 static const char *ascii4[256] = {
100 	"<NUL>", "<SOH>", "<STX>", "<ETX>", "<EOT>", "<ENQ>", "<ACK>", "<BEL>",
101 	"<BS>",  "<TAB>", "<LF>\n",  "<VT>", "<FF>",  "<CR>",  "<SO>",  "<SI>",
102 	"<DLE>", "<DC1>", "<DC2>", "<DC3>", "<DC4>", "<NAK>", "<SYN>", "<ETB>",
103 	"<CAN>", "<EM>",  "<SUB>", "<ESC>", "<FS>",  "<GS>",  "<RS>",  "<US>",
104 	" ",     "!",     "\"",    "#",     "$",     "%",     "&",     "\'",
105 	"(",     ")",     "*",     "+",     ",",     "-",     ".",     "/",
106 	"0",     "1",     "2",     "3",     "4",     "5",     "6",     "7",
107 	"8",     "9",     ":",     ";",     "<",     "=",     ">",     "?",
108 	"@",     "A",     "B",     "C",     "D",     "E",     "F",     "G",
109 	"H",     "I",     "J",     "K",     "L",     "M",     "N",     "O",
110 	"P",     "Q",     "R",     "S",     "T",     "U",     "V",     "W",
111 	"X",     "Y",     "Z",     "[",     "\\",    "]",     "^",     "_",
112 	"`",     "a",     "b",     "c",     "d",     "e",     "f",     "g",
113 	"h",     "i",     "j",     "k",     "l",     "m",     "n",     "o",
114 	"p",     "q",     "r",     "s",     "t",     "u",     "v",     "w",
115 	"x",     "y",     "z",     "{",     "|",     "}",     "~",     "<DEL>",
116 	"<128>", "<129>", "<130>", "<131>", "<132>", "<133>", "<134>", "<135>",
117 	"<136>", "<137>", "<138>", "<139>", "<140>", "<141>", "<142>", "<143>",
118 	"<144>", "<145>", "<146>", "<147>", "<148>", "<149>", "<150>", "<151>",
119 	"<152>", "<153>", "<154>", "<155>", "<156>", "<157>", "<158>", "<159>",
120 	"<160>", "<161>", "<162>", "<163>", "<164>", "<165>", "<166>", "<167>",
121 	"<168>", "<169>", "<170>", "<171>", "<172>", "<173>", "<174>", "<175>",
122 	"<176>", "<177>", "<178>", "<179>", "<180>", "<181>", "<182>", "<183>",
123 	"<184>", "<185>", "<186>", "<187>", "<188>", "<189>", "<190>", "<191>",
124 	"<192>", "<193>", "<194>", "<195>", "<196>", "<197>", "<198>", "<199>",
125 	"<200>", "<201>", "<202>", "<203>", "<204>", "<205>", "<206>", "<207>",
126 	"<208>", "<209>", "<210>", "<211>", "<212>", "<213>", "<214>", "<215>",
127 	"<216>", "<217>", "<218>", "<219>", "<220>", "<221>", "<222>", "<223>",
128 	"<224>", "<225>", "<226>", "<227>", "<228>", "<229>", "<230>", "<231>",
129 	"<232>", "<233>", "<234>", "<235>", "<236>", "<237>", "<238>", "<239>",
130 	"<240>", "<241>", "<242>", "<243>", "<244>", "<245>", "<246>", "<247>",
131 	"<248>", "<249>", "<250>", "<251>", "<252>", "<253>", "<254>", "<255>"
132 };
133 
134 static std::string hout(std::string s)
135 {
136 	static std::string sout;
137 	sout.clear();
138 	for (size_t n = 0; n < s.length(); n++)
139 		sout.append(ascii4[int(s[n])]);
140 	return sout;
141 }
142 */
143 
144 static void substitute (std::string &s, size_t &start, size_t end, std::string sub)
145 {
146 	if (start && s[start - 1] == '\n') {
147 		start--;
148 	}
149 //std::cout << "--------------------------------------------------------" << std::endl;
150 	if (sub.empty()) {
151 //std::cout << "ERASED:" << hout(s.substr(start, end - start + 1)) << std::endl;
152 		s.erase(start, end - start + 1);
153 	} else {
154 //std::cout << "REPLACED: '" << hout(s.substr(start, end - start + 1)) <<
155 //			 "' WITH '" << hout(sub) << "'" << std::endl;
156 		s.replace(start, end - start + 1, sub);
157 	}
158 //std::cout << "--------------------------------------------------------" << std::endl;
159 //std::cout << hout(s) << std::endl;
160 //std::cout << "=========================================================" << std::endl;
161 }
162 
163 static void add_text(std::string text)
164 {
165 	if (buffered) {
166 		buffered_macro.append(text);
167 		buffered_pointer = 0;
168 	} else {
169 		TransmitText->add_text(text);
170 	}
171 }
172 
173 void clear_buffered_text()
174 {
175 	buffered_macro.clear();
176 }
177 
178 char next_buffered_macro_char()
179 {
180 	if (buffered_macro.empty())
181 		return 0;
182 	char c = buffered_macro[buffered_pointer++];
183 	if (buffered_pointer >= buffered_macro.length()) {
184 		buffered_macro.clear();
185 		buffered_pointer = 0;
186 	}
187 	return c;
188 }
189 
190 static void pBUFFERED(std::string &s, size_t &i, size_t endbracket)
191 {
192 	substitute(s, i, endbracket, "");
193 	buffered = true;
194 	buffered_macro.clear();
195 }
196 
197 static void setwpm(double d)
198 {
199 	sldrCWxmtWPM->value(d);
200 	cntCW_WPM->value(d);
201 	progdefaults.CWusefarnsworth = false;
202 	btnCWusefarnsworth->value(0);
203 	que_ok = true;
204 }
205 
206 static void setfwpm(double d)
207 {
208 	sldrCWfarnsworth->value(d);
209 	progdefaults.CWusefarnsworth = true;
210 	btnCWusefarnsworth->value(1);
211 	que_ok = true;
212 }
213 
214 // following used for debugging and development
215 void push_txcmd(CMDS cmd)
216 {
217 	LOG_INFO("%s, # = %d", cmd.cmd.c_str(), (int)Tx_cmds.size());
218 	Tx_cmds.push(cmd);
219 }
220 
221 void push_rxcmd(CMDS cmd)
222 {
223 	LOG_INFO("%s, # = %d", cmd.cmd.c_str(), (int)Rx_cmds.size());
224 	Rx_cmds.push(cmd);
225 }
226 
227 // these variables are referenced outside of this file
228 MACROTEXT macros;
229 CONTESTCNTR contest_count;
230 
231 std::string qso_time = "";
232 std::string qso_exchange = "";
233 std::string exec_date = "";
234 std::string exec_time = "";
235 std::string exec_string = "";
236 std::string until_date = "";
237 std::string until_time = "";
238 std::string info1msg = "";
239 std::string info2msg = "";
240 std::string text2repeat = "";
241 
242 size_t repeatchar = 0;
243 
244 bool macro_idle_on = false;
245 bool macro_rx_wait = false;
246 
247 static float  idleTime = 0;
248 static bool TransmitON = false;
249 static bool ToggleTXRX = false;
250 static int mNbr;
251 
252 static std::string text2send = "";
253 
254 static size_t  xbeg = 0, xend = 0;
255 
256 static bool save_xchg;
257 static bool expand;
258 static bool GET = false;
259 static bool timed_exec = false;
260 static bool run_until = false;
261 static bool within_exec = false;
262 
263 bool local_timed_exec = false;
264 
265 void rx_que_continue(void *);
266 
267 static void postQueue(std::string s)
268 {
269 	s.append("\n");
270 	if (!progdefaults.macro_post) return;
271 	if (active_modem->get_mode() == MODE_IFKP)
272 		ifkp_rx_text->addstr(s, FTextBase::CTRL);
273 	else if (active_modem->get_mode() == MODE_FSQ)
274 		fsq_rx_text->addstr(s, FTextBase::CTRL);
275 	else
276 		ReceiveText->addstr(s, FTextBase::CTRL);
277 }
278 
279 static const char cutnumbers[] = "T12345678N";
280 static std::string cutstr;
281 
282 static std::string cut_string(const char *s)
283 {
284 	cutstr = s;
285 	if (!progdefaults.cutnbrs || active_modem != cw_modem)
286 		return cutstr;
287 
288 	for (size_t i = 0; i < cutstr.length(); i++)
289 		if (cutstr[i] >= '0' && cutstr[i] <= '9')
290 			cutstr[i] = cutnumbers[cutstr[i] - '0'];
291 	return cutstr;
292 
293 }
294 
295 static size_t mystrftime( char *s, size_t max, const char *fmt, const struct tm *tm) {
296 	return strftime(s, max, fmt, tm);
297 }
298 
299 static std::string CPSstring = "\
300 =============================================\n\
301 ABCDEFGHIJKLMN OPQRSTUVWXYZ\n\
302 abcdefghijklmn opqrstuvwxyz\n\
303 0123456789       9876543210\n\
304 !@#$%&*()_+-=[]{}\\|;:'\",.<>/?\n\
305 =============================================\n\
306 \n\
307 The Jaberwocky\n\
308 \n\
309 'Twas brillig, and the slithy toves\n\
310 Did gyre and gimble in the wabe;\n\
311 All mimsy were the borogoves,\n\
312 And the mome raths outgrabe.\n\
313 \n\
314 \"Beware the Jabberwock, my son!\n\
315 The jaws that bite, the claws that catch!\n\
316 Beware the Jubjub bird, and shun\n\
317 The frumious Bandersnatch!\"\n\
318 \n\
319 He took his vorpal sword in hand:\n\
320 Long time the manxome foe he sought-\n\
321 So rested he by the Tumtum tree,\n\
322 And stood awhile in thought.\n\
323 \n\
324 And as in uffish thought he stood,\n\
325 The Jabberwock, with eyes of flame,\n\
326 Came whiffling through the tulgey wood,\n\
327 And burbled as it came!\n\
328 \n\
329 One, two! One, two! and through and through\n\
330 The vorpal blade went snicker-snack!\n\
331 He left it dead, and with its head\n\
332 He went galumphing back.\n\
333 \n\
334 \"And hast thou slain the Jabberwock?\n\
335 Come to my arms, my beamish boy!\n\
336 O frabjous day! Callooh! Callay!\"\n\
337 He chortled in his joy.\n\
338 \n\
339 'Twas brillig, and the slithy toves\n\
340 Did gyre and gimble in the wabe;\n\
341 All mimsy were the borogoves,\n\
342 And the mome raths outgrabe.\n";
343 
344 static std::string ccode = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
345 
346 bool PERFORM_CPS_TEST = false;
347 int  num_cps_chars = 0;
348 string testfilename;
349 
350 void CPS_report(int samples, int prepost)
351 {
352 	char specs[1000];
353 	memset(specs, 0, 1000);
354 	std::string results = "\nCPS test\ntext:         ";
355 	if (testfilename[0] != '\n')
356 		results.append("\n");
357 	results.append(testfilename).append("\n");
358 	string strout;
359 	double xmttime = 1.0 * samples / active_modem->get_samplerate();
360 	double overhead = 1.0 * prepost / active_modem->get_samplerate();
361 	num_cps_chars--;
362 	snprintf(specs, sizeof(specs), "\
363 mode:         %s\n\
364 # chars:      %d\n\
365 overhead:     %f sec\n\
366 xmt time:     %f sec\n\
367 xmt samples:  %d\n\
368 sample rate:  %d\n\
369 chars/sec:    %f",
370 			mode_info[active_modem->get_mode()].name,
371 			num_cps_chars,
372 			overhead,
373 			xmttime - overhead,
374 			samples,
375 			active_modem->get_samplerate(),
376 			num_cps_chars / (xmttime - overhead));
377 	results.append(specs);
378 	LOG_INFO("%s", results.c_str());
379 	results.append("\n");
380 	if (active_modem->get_mode() == MODE_IFKP)
381 		ifkp_rx_text->add(results.c_str(), FTextBase::ALTR);
382 	else if (active_modem->get_mode() == MODE_FSQ)
383 		fsq_rx_text->addstr(results.c_str(), FTextBase::ALTR);
384 	else
385 		ReceiveText->add(results.c_str(), FTextBase::ALTR);
386 	PERFORM_CPS_TEST = false;
387 }
388 
389 static void pCPS_TEST(std::string &s, size_t &i, size_t endbracket)
390 {
391 	trx_mode id = active_modem->get_mode();
392 	if ( id == MODE_SSB || id == MODE_WWV ||
393 		id == MODE_ANALYSIS ||
394 		id == MODE_WEFAX_576 || id == MODE_WEFAX_288 ||
395 		id == MODE_SITORB || id == MODE_NAVTEX ) {
396 		if (active_modem->get_mode() == MODE_IFKP)
397 			ifkp_tx_text->add("Mode not supported\n", FTextBase::ALTR);
398 		else if (active_modem->get_mode() == MODE_FSQ)
399 			fsq_rx_text->addstr("Mode not supported", FTextBase::ALTR);
400 		else
401 			ReceiveText->add("Mode not supported\n", FTextBase::ALTR);
402 		s.clear();
403 		return;
404 	}
405 
406 	std::string buffer = s.substr(i+10, endbracket - i - 10);
407 	s.clear();
408 
409 	int n;
410 	if (buffer.empty()) n = 10;
411 	sscanf(buffer.c_str(), "%d", &n);
412 	if (n <= 0) n = 10;
413 	if (n > 100) n = 100;
414 
415 // sample count with 'n' characters
416 	int s1[256];
417 	for (int i = 0; i < 256; i++) s1[i] = 0;
418 // converstion from sample count to milliseconds
419 	double k = 1000.0 / (active_modem->get_samplerate() * n);
420 
421 	stopMacroTimer();
422 	active_modem->set_stopflag(false);
423 
424 	PERFORM_CPS_TEST = true;
425 	int s0 = number_of_samples("");
426 // sample count for characters ' ' through '~'
427 	for(int j = 0; j < 256; j++) {
428 		s1[j] = number_of_samples(string(n, j)) - s0;
429 	}
430 	PERFORM_CPS_TEST = false;
431 
432 // report generator
433 	char results[200];
434 	string line_out;
435 	snprintf(results, sizeof(results), "\nCPS test\nMode : %s\n", mode_info[active_modem->get_mode()].name);
436 	line_out = results;
437 	snprintf(results, sizeof(results), "Based on %d character string\n", n);
438 	line_out.append(results);
439 	snprintf(results, sizeof(results), "Overhead = %.3f msec\n", 1000.0 * s0 / active_modem->get_samplerate());
440 	line_out.append(results);
441 	for (int j = 0, ln = 0; j < 256; j++ ) {
442 		snprintf(results, sizeof(results), "%2x%8.2f", j, k * s1[j]);
443 		line_out.append(results);
444 		ln++;
445 		if (ln && (ln % 4 == 0)) line_out.append("\n");
446 		else line_out.append(" | ");
447 	}
448 	if (!line_out.empty()) {
449 		LOG_INFO("%s", line_out.c_str());
450 		if (active_modem->get_mode() == MODE_IFKP)
451 			ifkp_rx_text->add(line_out.c_str(), FTextBase::ALTR);
452 		if (active_modem->get_mode() == MODE_FSQ)
453 			fsq_rx_text->add(line_out.c_str(), FTextBase::ALTR);
454 		else
455 			ReceiveText->add(line_out.c_str(), FTextBase::ALTR);
456 	}
457 	return;
458 }
459 
460 static void pCPS_FILE(std::string &s, size_t &i, size_t endbracket)
461 {
462 	trx_mode id = active_modem->get_mode();
463 	if ( id == MODE_SSB || id == MODE_WWV ||
464 		id == MODE_ANALYSIS ||
465 		id == MODE_WEFAX_576 || id == MODE_WEFAX_288 ||
466 		id == MODE_SITORB || id == MODE_NAVTEX ) {
467 		if (active_modem->get_mode() == MODE_IFKP)
468 			ifkp_rx_text->add("Mode not supported\n", FTextBase::ALTR);
469 		else if (active_modem->get_mode() == MODE_FSQ)
470 			fsq_rx_text->add("Mode not supported\n", FTextBase::ALTR);
471 		else
472 			ReceiveText->add("Mode not supported\n", FTextBase::ALTR);
473 		s.clear();
474 		return;
475 	}
476 
477 	std::string fname = s.substr(i+10, endbracket - i - 10);
478 	if (fname.length() > 0 && !within_exec) {
479 		FILE *toadd = fl_fopen(fname.c_str(), "r");
480 		if (toadd) {
481 			std::string buffer;
482 			char c = getc(toadd);
483 			while (c && !feof(toadd)) {
484 				if (c != '\r') buffer += c; // damn MSDOS txt files
485 				c = getc(toadd);
486 			}
487 			s.clear();
488 			fclose(toadd);
489 			if (active_modem->get_mode() == MODE_IFKP)
490 				ifkp_tx_text->clear();
491 			else if (active_modem->get_mode() == MODE_FSQ)
492 				fsq_tx_text->clear();
493 			else
494 				TransmitText->clear();
495 			testfilename = fname;
496 
497 			stopMacroTimer();
498 			active_modem->set_stopflag(false);
499 
500 			PERFORM_CPS_TEST = true;
501 			int s0 = number_of_samples("");
502 			num_cps_chars = 0;
503 			CPS_report(number_of_samples(buffer), s0);
504 			PERFORM_CPS_TEST = false;
505 
506 		} else {
507 			string resp = "Could not locate ";
508 			resp.append(fname).append("\n");
509 			if (active_modem->get_mode() == MODE_IFKP)
510 				ifkp_rx_text->add(resp.c_str(), FTextBase::ALTR);
511 			else if (active_modem->get_mode() == MODE_FSQ)
512 				fsq_rx_text->add(resp.c_str(), FTextBase::ALTR);
513 			else
514 				ReceiveText->add(resp.c_str(), FTextBase::ALTR);
515 			LOG_WARN("%s not found", fname.c_str());
516 			substitute(s, i, endbracket, "");
517 			PERFORM_CPS_TEST = false;
518 		}
519 	} else {
520 		PERFORM_CPS_TEST = false;
521 		s.clear();
522 	}
523 }
524 
525 static void pCPS_STRING(std::string &s, size_t &i, size_t endbracket)
526 {
527 	trx_mode id = active_modem->get_mode();
528 	if ( id == MODE_SSB || id == MODE_WWV ||
529 		id == MODE_ANALYSIS ||
530 		id == MODE_WEFAX_576 || id == MODE_WEFAX_288 ||
531 		id == MODE_SITORB || id == MODE_NAVTEX ) {
532 		if (active_modem->get_mode() == MODE_IFKP)
533 			ifkp_rx_text->add("Mode not supported\n", FTextBase::ALTR);
534 		else if (active_modem->get_mode() == MODE_FSQ)
535 			fsq_rx_text->add("Mode not supported\n", FTextBase::ALTR);
536 		else
537 			ReceiveText->add("Mode not supported\n", FTextBase::ALTR);
538 		s.clear();
539 		return;
540 	}
541 
542 	std::string buffer = s.substr(i+12, endbracket - i - 12);
543 	std::string txtbuf = buffer;
544 	s.clear();
545 	size_t p = buffer.find("\\n");
546 	while (p != string::npos) {
547 		buffer.replace(p,2,"\n");
548 		p = buffer.find("\\n");
549 	}
550 	if (buffer.length()) {
551 		if (active_modem->get_mode() == MODE_IFKP)
552 			ifkp_tx_text->clear();
553 		else if (active_modem->get_mode() == MODE_FSQ)
554 			fsq_tx_text->clear();
555 		else
556 			TransmitText->clear();
557 
558 		stopMacroTimer();
559 		active_modem->set_stopflag(false);
560 
561 		PERFORM_CPS_TEST = true;
562 		int s0 = number_of_samples("");
563 		num_cps_chars = 0;
564 		testfilename = txtbuf;
565 		CPS_report(number_of_samples(buffer), s0);
566 		PERFORM_CPS_TEST = false;
567 
568 	} else {
569 		string resp = "Text not specified";
570 		LOG_WARN("%s", resp.c_str());
571 		resp.append("\n");
572 		if (active_modem->get_mode() == MODE_IFKP)
573 			ifkp_rx_text->add(resp.c_str(), FTextBase::ALTR);
574 		else if (active_modem->get_mode() == MODE_FSQ)
575 			fsq_rx_text->add(resp.c_str(), FTextBase::ALTR);
576 		else
577 			ReceiveText->add(resp.c_str(), FTextBase::ALTR);
578 		PERFORM_CPS_TEST = false;
579 	}
580 }
581 
582 static void pCPS_N(std::string &s, size_t &i, size_t endbracket)
583 {
584 	trx_mode id = active_modem->get_mode();
585 	if ( id == MODE_SSB || id == MODE_WWV ||
586 		id == MODE_ANALYSIS ||
587 		id == MODE_WEFAX_576 || id == MODE_WEFAX_288 ||
588 		id == MODE_SITORB || id == MODE_NAVTEX ) {
589 		if (active_modem->get_mode() == MODE_IFKP)
590 			ifkp_rx_text->add("Mode not supported\n", FTextBase::ALTR);
591 		else if (active_modem->get_mode() == MODE_FSQ)
592 			fsq_rx_text->add("Mode not supported\n", FTextBase::ALTR);
593 		else
594 			ReceiveText->add("Mode not supported\n", FTextBase::ALTR);
595 		s.clear();
596 		return;
597 	}
598 
599 	std::string buffer = s.substr(i+7, endbracket - i - 7);
600 	s.clear();
601 
602 	if (buffer.empty()) return;
603 
604 	int numgroups, wc, cc, cl;
605 	cl = ccode.length();
606 
607 	sscanf(buffer.c_str(), "%d", &numgroups);
608 	if (numgroups <= 0 || numgroups > 100000) numgroups = 100;
609 
610 	srand(time(0));
611 	buffer.clear();
612 	for (wc = 1; wc <= numgroups; wc++) {
613 		for (cc = 0; cc < 5; cc++) {
614 			buffer += ccode[ rand() % cl ];
615 		}
616 		if (wc % 10 == 0) buffer += '\n';
617 		else buffer += ' ';
618 	}
619 
620 	if (active_modem->get_mode() == MODE_IFKP)
621 		ifkp_tx_text->clear();
622 	else if (active_modem->get_mode() == MODE_FSQ)
623 		fsq_tx_text->clear();
624 	else
625 		TransmitText->clear();
626 
627 	stopMacroTimer();
628 	active_modem->set_stopflag(false);
629 
630 	PERFORM_CPS_TEST = true;
631 	int s0 = number_of_samples("");
632 	num_cps_chars = 0;
633 	testfilename = "Random group test";
634 	CPS_report(number_of_samples(buffer), s0);
635 	PERFORM_CPS_TEST = false;
636 
637 	return;
638 }
639 
640 static void pWAV_TEST(std::string &s, size_t &i, size_t endbracket)
641 {
642 	s.clear();
643 	trx_mode id = active_modem->get_mode();
644 	if ( id == MODE_SSB || id == MODE_WWV ||
645 		id == MODE_ANALYSIS ||
646 		id == MODE_WEFAX_576 || id == MODE_WEFAX_288 ||
647 		id == MODE_SITORB || id == MODE_NAVTEX ) {
648 		if (active_modem->get_mode() == MODE_IFKP)
649 			ifkp_rx_text->add("Mode not supported\n", FTextBase::ALTR);
650 		else if (active_modem->get_mode() == MODE_FSQ)
651 			fsq_rx_text->add("Mode not supported\n", FTextBase::ALTR);
652 		else
653 			ReceiveText->add("Mode not supported\n", FTextBase::ALTR);
654 		return;
655 	}
656 	testfilename = "internal string";
657 
658 	stopMacroTimer();
659 	active_modem->set_stopflag(false);
660 
661 	PERFORM_CPS_TEST = true;
662 	trx_transmit();
663 	number_of_samples(CPSstring);
664 	PERFORM_CPS_TEST = false;
665 }
666 
667 static void pWAV_N(std::string &s, size_t &i, size_t endbracket)
668 {
669 	trx_mode id = active_modem->get_mode();
670 	if ( id == MODE_SSB || id == MODE_WWV ||
671 		id == MODE_ANALYSIS ||
672 		id == MODE_WEFAX_576 || id == MODE_WEFAX_288 ||
673 		id == MODE_SITORB || id == MODE_NAVTEX ) {
674 		if (active_modem->get_mode() == MODE_IFKP)
675 			ifkp_rx_text->add("Mode not supported\n", FTextBase::ALTR);
676 		else if (active_modem->get_mode() == MODE_FSQ)
677 			fsq_rx_text->add("Mode not supported\n", FTextBase::ALTR);
678 		else
679 			ReceiveText->add("Mode not supported\n", FTextBase::ALTR);
680 		s.clear();
681 		return;
682 	}
683 
684 	std::string buffer = s.substr(i+7, endbracket - i - 7);
685 	s.clear();
686 
687 	if (buffer.empty()) return;
688 
689 	int numgroups, wc, cc, cl;
690 	cl = ccode.length();
691 
692 	sscanf(buffer.c_str(), "%d", &numgroups);
693 	if (numgroups <= 0 || numgroups > 100000) numgroups = 100;
694 
695 	srand(time(0));
696 	buffer.clear();
697 	for (wc = 1; wc <= numgroups; wc++) {
698 		for (cc = 0; cc < 5; cc++) {
699 			buffer += ccode[ rand() % cl ];
700 		}
701 		if (wc % 10 == 0) buffer += '\n';
702 		else buffer += ' ';
703 	}
704 
705 	if (active_modem->get_mode() == MODE_IFKP)
706 		ifkp_tx_text->clear();
707 	else if (active_modem->get_mode() == MODE_FSQ)
708 		fsq_tx_text->clear();
709 	else
710 		TransmitText->clear();
711 
712 	stopMacroTimer();
713 	active_modem->set_stopflag(false);
714 	PERFORM_CPS_TEST = true;
715 	trx_transmit();
716 	number_of_samples(buffer);
717 	PERFORM_CPS_TEST = false;
718 	return;
719 
720 }
721 
722 static void pWAV_FILE(std::string &s, size_t &i, size_t endbracket)
723 {
724 	trx_mode id = active_modem->get_mode();
725 	if ( id == MODE_SSB || id == MODE_WWV ||
726 		id == MODE_ANALYSIS ||
727 		id == MODE_WEFAX_576 || id == MODE_WEFAX_288 ||
728 		id == MODE_SITORB || id == MODE_NAVTEX ) {
729 		if (active_modem->get_mode() == MODE_IFKP)
730 			ifkp_rx_text->add("Mode not supported\n", FTextBase::ALTR);
731 		else if (active_modem->get_mode() == MODE_FSQ)
732 			fsq_rx_text->add("Mode not supported\n", FTextBase::ALTR);
733 		else
734 			ReceiveText->add("Mode not supported\n", FTextBase::ALTR);
735 		s.clear();
736 		return;
737 	}
738 
739 	std::string fname = s.substr(i+10, endbracket - i - 10);
740 	if (fname.length() > 0 && !within_exec) {
741 		FILE *toadd = fl_fopen(fname.c_str(), "r");
742 		if (toadd) {
743 			std::string buffer;
744 			char c = getc(toadd);
745 			while (c && !feof(toadd)) {
746 				if (c != '\r') buffer += c; // damn MSDOS txt files
747 				c = getc(toadd);
748 			}
749 			s.clear();
750 			fclose(toadd);
751 			if (active_modem->get_mode() == MODE_IFKP)
752 				ifkp_tx_text->clear();
753 			else if (active_modem->get_mode() == MODE_FSQ)
754 				fsq_tx_text->clear();
755 			else
756 				TransmitText->clear();
757 			testfilename = fname;
758 
759 			stopMacroTimer();
760 			active_modem->set_stopflag(false);
761 			PERFORM_CPS_TEST = true;
762 			trx_transmit();
763 
764 			number_of_samples(buffer);
765 			PERFORM_CPS_TEST = false;
766 
767 		} else {
768 			string resp = "Could not locate ";
769 			resp.append(fname).append("\n");
770 			if (active_modem->get_mode() == MODE_IFKP)
771 				ifkp_rx_text->add(resp.c_str(), FTextBase::ALTR);
772 			else if (active_modem->get_mode() == MODE_FSQ)
773 				fsq_rx_text->add(resp.c_str(), FTextBase::ALTR);
774 			else
775 				ReceiveText->add(resp.c_str(), FTextBase::ALTR);
776 			LOG_WARN("%s not found", fname.c_str());
777 			substitute(s, i, endbracket, "");
778 			PERFORM_CPS_TEST = false;
779 		}
780 	} else {
781 		PERFORM_CPS_TEST = false;
782 		s.clear();
783 	}
784 }
785 
786 static void pWAV_STRING(std::string &s, size_t &i, size_t endbracket)
787 {
788 	trx_mode id = active_modem->get_mode();
789 	if ( id == MODE_SSB || id == MODE_WWV ||
790 		id == MODE_ANALYSIS ||
791 		id == MODE_WEFAX_576 || id == MODE_WEFAX_288 ||
792 		id == MODE_SITORB || id == MODE_NAVTEX ) {
793 		if (active_modem->get_mode() == MODE_IFKP)
794 			ifkp_rx_text->add("Mode not supported\n", FTextBase::ALTR);
795 		else if (active_modem->get_mode() == MODE_FSQ)
796 			fsq_rx_text->add("Mode not supported\n", FTextBase::ALTR);
797 		else
798 			ReceiveText->add("Mode not supported\n", FTextBase::ALTR);
799 		s.clear();
800 		return;
801 	}
802 
803 	std::string buffer = s.substr(i+12, endbracket - i - 12);
804 	std::string txtbuf = buffer;
805 	s.clear();
806 	size_t p = buffer.find("\\n");
807 	while (p != string::npos) {
808 		buffer.replace(p,2,"\n");
809 		p = buffer.find("\\n");
810 	}
811 	if (buffer.length()) {
812 		if (active_modem->get_mode() == MODE_IFKP)
813 			ifkp_tx_text->clear();
814 		else if (active_modem->get_mode() == MODE_FSQ)
815 			fsq_tx_text->clear();
816 		else
817 			TransmitText->clear();
818 
819 		stopMacroTimer();
820 		active_modem->set_stopflag(false);
821 		PERFORM_CPS_TEST = true;
822 		trx_transmit();
823 
824 		testfilename = txtbuf;
825 		number_of_samples(buffer);
826 		PERFORM_CPS_TEST = false;
827 	} else {
828 		string resp = "Text not specified";
829 		LOG_WARN("%s", resp.c_str());
830 		resp.append("\n");
831 		if (active_modem->get_mode() == MODE_IFKP)
832 			ifkp_rx_text->add(resp.c_str(), FTextBase::ALTR);
833 		else if (active_modem->get_mode() == MODE_FSQ)
834 			fsq_rx_text->add(resp.c_str(), FTextBase::ALTR);
835 		else
836 			ReceiveText->add(resp.c_str(), FTextBase::ALTR);
837 		PERFORM_CPS_TEST = false;
838 	}
839 }
840 
841 static void pCOMMENT(std::string &s, size_t &i, size_t endbracket)
842 {
843 	substitute(s, i, endbracket, "");
844 	if (s[i] == '\n') i++;
845 }
846 
847 static void pFILE(std::string &s, size_t &i, size_t endbracket)
848 {
849 	std::string fname = s.substr(i+6, endbracket - i - 6);
850 	if (fname.length() > 0 && !within_exec) {
851 		FILE *toadd = fl_fopen(fname.c_str(), "r");
852 		if (toadd) {
853 			std::string buffer;
854 			char c = getc(toadd);
855 			while (c && !feof(toadd)) {
856 				if (c != '\r') buffer += c; // change CRLF to LF
857 				c = getc(toadd);
858 			}
859 			size_t blen = buffer.length();
860 			if (blen > 0) {
861 				if (!(buffer[blen -1] == ' ' || buffer[blen-1] == '\n'))
862 					buffer += ' ';
863 			}
864 			substitute(s, i, endbracket, buffer);
865 			fclose(toadd);
866 		} else {
867 			LOG_WARN("%s not found", fname.c_str());
868 			substitute(s, i, endbracket, "ERROR: CANNOT OPEN FILE");
869 		}
870 	} else {
871 		substitute(s, i, endbracket, "OH SHIT");
872 	}
873 }
874 
875 static notify_dialog *macro_alert_dialog = 0;
876 
877 static void doTIMER(std::string s)
878 {
879 	int number;
880 	std::string sTime = s.substr(7);
881 	if (sTime.length() > 0) {
882 		sscanf(sTime.c_str(), "%d", &number);
883 		int mtime = stop_macro_time();
884 		if (mtime >= number) {
885 			if (!macro_alert_dialog) macro_alert_dialog = new notify_dialog;
886 			ostringstream comment;
887 			comment << "Macro timer must be > macro duration of " << mtime << " secs";
888 			macro_alert_dialog->notify(comment.str().c_str(), 5.0);
889 			REQ(show_notifier, macro_alert_dialog);
890 			progStatus.skip_sked_macro = false;
891 		} else {
892 			progStatus.timer = number - mtime;
893 			progStatus.timerMacro = mNbr;
894 			progStatus.skip_sked_macro = true;
895 		}
896 	}
897 	que_ok = true;
898 }
899 
900 static void pTIMER(std::string &s, size_t &i, size_t endbracket)
901 {
902 	if (within_exec) {
903 		substitute(s, i, endbracket, "");
904 		return;
905 	}
906 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doTIMER };
907 	push_rxcmd(cmd);
908 	substitute(s, i, endbracket, "");
909 }
910 
911 static void doAFTER(std::string s)
912 {
913 	int number;
914 	std::string sTime = s.substr(7);
915 	if (sTime.length() > 0) {
916 		sscanf(sTime.c_str(), "%d", &number);
917 		progStatus.timer = number;
918 		progStatus.timerMacro = mNbr;
919 		progStatus.skip_sked_macro = true;
920 	}
921 	que_ok = true;
922 }
923 
924 static void pAFTER(std::string &s, size_t &i, size_t endbracket)
925 {
926 	if (within_exec) {
927 		substitute(s, i, endbracket, "");
928 		return;
929 	}
930 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doAFTER };
931 	push_rxcmd(cmd);
932 	substitute(s, i, endbracket, "");
933 }
934 
935 static void pREPEAT(std::string &s, size_t &i, size_t endbracket)
936 {
937 	if (within_exec) {
938 		substitute(s, i, endbracket, "");
939 		return;
940 	}
941 	progStatus.repeatMacro = mNbr;
942 	substitute(s, i, endbracket, "");
943 	text2repeat = s;
944 	repeatchar = 0;
945 	s.insert(i, "[REPEAT]");
946 }
947 
948 static void pWPM(std::string &s, size_t &i, size_t endbracket)
949 {
950 	if (within_exec) {
951 		substitute(s, i, endbracket, "");
952 		return;
953 	}
954 	float number;
955 	std::string snumber = s.substr(i+5, endbracket - i - 5);
956 
957 	if (snumber.length() > 0) {
958 
959 		// first value = WPM
960 		sscanf(snumber.c_str(), "%f", &number);
961 		if (number < 5) number = 5;
962 		if (number > 200) number = 200;
963 		progdefaults.CWspeed = number;
964 		setwpm(number);
965 
966 		// second value = Farnsworth WPM
967 		size_t pos;
968 		if ((pos = snumber.find(":")) != std::string::npos) {
969 			snumber.erase(0, pos+1);
970 			if (snumber.length())
971 				sscanf(snumber.c_str(), "%f", &number);
972 			if (number < 5) number = 5;
973 			if (number > progdefaults.CWspeed) number = progdefaults.CWspeed;
974 			progdefaults.CWfarnsworth = number;
975 			if (number == progdefaults.CWspeed)
976 				setwpm(number);
977 			else
978 				setfwpm(number);
979 		}
980 	}
981 
982 	substitute(s, i, endbracket, "");
983 }
984 
985 static void pRISETIME(std::string &s, size_t &i, size_t endbracket)
986 {
987 	if (within_exec) {
988 		substitute(s, i, endbracket, "");
989 		return;
990 	}
991 	float number;
992 	std::string sVal = s.substr(i+6, endbracket - i - 6);
993 	if (sVal.length() > 0) {
994 		sscanf(sVal.c_str(), "%f", &number);
995 		if (number < 0) number = 0;
996 		if (number > 20) number = 20;
997 		progdefaults.CWrisetime = number;
998 		cntCWrisetime->value(number);
999 	}
1000 	substitute(s, i, endbracket, "");
1001 }
1002 
1003 static void pPRE(std::string &s, size_t &i, size_t endbracket)
1004 {
1005 	if (within_exec) {
1006 		substitute(s, i, endbracket, "");
1007 		return;
1008 	}
1009 	float number;
1010 	std::string sVal = s.substr(i+5, endbracket - i - 5);
1011 	if (sVal.length() > 0) {
1012 		sscanf(sVal.c_str(), "%f", &number);
1013 		if (number < 0) number = 0;
1014 		if (number > 20) number = 20;
1015 		progdefaults.CWpre = number;
1016 		cntPreTiming->value(number);
1017 	}
1018 	substitute(s, i, endbracket, "");
1019 }
1020 
1021 static void pPOST(std::string &s, size_t &i, size_t endbracket)
1022 {
1023 	if (within_exec) {
1024 		substitute(s, i, endbracket, "");
1025 		return;
1026 	}
1027 	float number;
1028 	std::string sVal = s.substr(i+6, endbracket - i - 6);
1029 	if (sVal.length() > 0) {
1030 		sscanf(sVal.c_str(), "%f", &number);
1031 		if (number < -20) number = -20;
1032 		if (number > 20) number = 20;
1033 		progdefaults.CWpost = number;
1034 		cntPostTiming->value(number);
1035 	}
1036 	substitute(s, i, endbracket, "");
1037 }
1038 
1039 static void doWPM(std::string s)
1040 {
1041 	float number;
1042 	std::string snumber = s.substr(6);
1043 
1044 	if (snumber.length() > 0) {
1045 
1046 		// first value = WPM
1047 		sscanf(snumber.c_str(), "%f", &number);
1048 		if (number < 5) number = 5;
1049 		if (number > 200) number = 200;
1050 		progdefaults.CWspeed = number;
1051 		REQ(setwpm, number);
1052 
1053 		// second value = Farnsworth WPM
1054 		size_t pos;
1055 		if ((pos = snumber.find(":")) != std::string::npos) {
1056 			snumber.erase(0, pos+1);
1057 			if (snumber.length())
1058 				sscanf(snumber.c_str(), "%f", &number);
1059 			if (number < 5) number = 5;
1060 			if (number > progdefaults.CWspeed) number = progdefaults.CWspeed;
1061 			progdefaults.CWfarnsworth = number;
1062 			if (number == progdefaults.CWspeed)
1063 				REQ(setwpm, number);
1064 			else
1065 				REQ(setfwpm, number);
1066 		}
1067 	}
1068 
1069 }
1070 
1071 static void pTxQueWPM(std::string &s, size_t &i, size_t endbracket)
1072 {
1073 	if (within_exec) {
1074 		substitute(s, i, endbracket, "");
1075 		return;
1076 	}
1077 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doWPM };
1078 	push_txcmd(cmd);
1079 	substitute(s, i, endbracket, "^!");
1080 }
1081 
1082 struct STRpush {
1083 	string smode;
1084 	int    freq;
1085 	STRpush() { smode = ""; freq = -1; }
1086 };
1087 
1088 stack<STRpush> mf_stack;
1089 
1090 //static string mf_stack = "";
1091 
1092 static void mMODEM(std::string s)
1093 {
1094 	trx_mode m;
1095 	s = ucasestr(s);
1096 	for (m = 0; m < NUM_MODES; m++)
1097 		if (s == ucasestr(mode_info[m].sname))
1098 			break;
1099 	if (m == NUM_MODES) {
1100 		return;
1101 	}
1102 	if (active_modem->get_mode() != mode_info[m].mode)
1103 		init_modem_sync(mode_info[m].mode);
1104 }
1105 
1106 static void mFREQ(int f)
1107 {
1108 	active_modem->set_freq(f);
1109 }
1110 
1111 static void doPOP(std::string s)
1112 {
1113 	if (!mf_stack.empty()) {
1114 		STRpush psh = mf_stack.top();
1115 		mf_stack.pop();
1116 		LOG_INFO("%s, %d", psh.smode.c_str(), psh.freq);
1117 		if (psh.freq != -1) mFREQ(psh.freq);
1118 		if (!psh.smode.empty()) mMODEM(psh.smode);
1119 	} else
1120 		LOG_INFO("%s", "stack empty");
1121 	que_ok = true;
1122 }
1123 
1124 static void pPOP(std::string &s, size_t &i, size_t endbracket)
1125 {
1126 	if (!mf_stack.empty()) {
1127 		STRpush psh = mf_stack.top();
1128 		mf_stack.pop();
1129 		LOG_INFO("%s, %d", psh.smode.c_str(), psh.freq);
1130 		if (psh.freq != -1) mFREQ(psh.freq);
1131 		if (!psh.smode.empty()) mMODEM(psh.smode);
1132 	} else
1133 		LOG_INFO("%s", "stack empty");
1134 	substitute(s, i, endbracket, "");
1135 }
1136 
1137 static void pTxQuePOP(std::string &s, size_t &i, size_t endbracket)
1138 {
1139 	if (within_exec) {
1140 		substitute(s, i, endbracket, "");
1141 		return;
1142 	}
1143 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doPOP };
1144 	push_txcmd(cmd);
1145 	substitute(s, i, endbracket, "^!");
1146 }
1147 
1148 static void pRxQuePOP(std::string &s, size_t &i, size_t endbracket)
1149 {
1150 	if (within_exec) {
1151 		substitute(s, i, endbracket, "");
1152 		return;
1153 	}
1154 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doPOP };
1155 	push_rxcmd(cmd);
1156 	substitute(s, i, endbracket, "");
1157 }
1158 
1159 static void doPUSHmode(std::string s)
1160 {
1161 	STRpush psh;
1162 	if (s[5] == '>') {
1163 		psh.smode = mode_info[active_modem->get_mode()].sname;
1164 		psh.freq = active_modem->get_freq();
1165 	} else {
1166 		if (s[5] == ':') {
1167 			if (s[6] == 'm' || s[7] == 'm')
1168 				psh.smode = mode_info[active_modem->get_mode()].sname;
1169 			if (s[6] == 'f' || s[7] == 'f')
1170 				psh.freq = active_modem->get_freq();
1171 		}
1172 	}
1173 	LOG_INFO("%s, %d", psh.smode.c_str(), psh.freq);
1174 	mf_stack.push(psh);
1175 	que_ok = true;
1176 }
1177 
1178 static void pTxQuePUSH(std::string &s, size_t &i, size_t endbracket)
1179 {
1180 	if (within_exec) {
1181 		substitute(s, i, endbracket, "");
1182 		return;
1183 	}
1184 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doPUSHmode };
1185 	push_txcmd(cmd);
1186 	substitute(s, i, endbracket, "^!");
1187 }
1188 
1189 static void pRxQuePUSH(std::string &s, size_t &i, size_t endbracket)
1190 {
1191 	if (within_exec) {
1192 		substitute(s, i, endbracket, "");
1193 		return;
1194 	}
1195 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doPUSHmode };
1196 	push_rxcmd(cmd);
1197 //	substitute(s, i, endbracket, "^!");
1198 	substitute(s, i, endbracket, "");
1199 }
1200 
1201 static void pPUSH(std::string &s, size_t &i, size_t endbracket)
1202 {
1203 	STRpush psh;
1204 	if (s[i+5] == '>') {
1205 		psh.smode = mode_info[active_modem->get_mode()].sname;
1206 		psh.freq = active_modem->get_freq();
1207 	} else {
1208 		if (s[i+5] == ':') {
1209 			if (s[i+6] == 'm' || s[i+7] == 'm')
1210 				psh.smode = mode_info[active_modem->get_mode()].sname;
1211 			if (s[i+6] == 'f' || s[i+7] == 'f')
1212 				psh.freq = active_modem->get_freq();
1213 		}
1214 	}
1215 	LOG_INFO("%s, %d", psh.smode.c_str(), psh.freq);
1216 	mf_stack.push(psh);
1217 	substitute(s, i, endbracket, "");
1218 	return;
1219 }
1220 
1221 static void pDIGI(std::string &s, size_t &i, size_t endbracket)
1222 {
1223 	s.replace(i, endbracket - i + 1, mode_info[active_modem->get_mode()].adif_name);
1224 }
1225 
1226 string macrochar = "";
1227 static void doTxDIGI(std::string s)
1228 {
1229 	macrochar = mode_info[active_modem->get_mode()].adif_name;
1230 	que_ok = true;
1231 }
1232 
1233 static void pTxDIGI(std::string &s, size_t &i, size_t endbracket)
1234 {
1235 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doTxDIGI };
1236 	push_txcmd(cmd);
1237 	substitute(s, i, endbracket, "^!");
1238 }
1239 
1240 static void doTxFREQ(std::string s)
1241 {
1242 	macrochar = inpFreq->value();
1243 	que_ok = true;
1244 }
1245 
1246 static void pTxFREQ(std::string &s, size_t &i, size_t endbracket)
1247 {
1248 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doTxFREQ };
1249 	push_txcmd(cmd);
1250 	substitute(s, i, endbracket, "^!");
1251 }
1252 
1253 static void setRISETIME(int d)
1254 {
1255 	cntCWrisetime->value(d);
1256 	que_ok = true;
1257 }
1258 
1259 static void doRISETIME(std::string s)
1260 {
1261 	float number;
1262 	std::string sVal = s.substr(7, s.length() - 8);
1263 	if (sVal.length() > 0) {
1264 		sscanf(sVal.c_str(), "%f", &number);
1265 		if (number < 0) number = 0;
1266 		if (number > 20) number = 20;
1267 		progdefaults.CWrisetime = number;
1268 		REQ(setRISETIME, number);
1269 	}
1270 }
1271 
1272 static void pTxQueRISETIME(std::string &s, size_t &i, size_t endbracket)
1273 {
1274 	if (within_exec) {
1275 		substitute(s, i, endbracket, "");
1276 		return;
1277 	}
1278 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRISETIME };
1279 	push_txcmd(cmd);
1280 	substitute(s, i, endbracket, "^!");
1281 }
1282 
1283 static void setPRE(int d)
1284 {
1285 	cntPreTiming->value(d);
1286 	que_ok = true;
1287 }
1288 
1289 static void doPRE(std::string s)
1290 {
1291 	float number;
1292 	std::string sVal = s.substr(6, s.length() - 7);
1293 	if (sVal.length() > 0) {
1294 		sscanf(sVal.c_str(), "%f", &number);
1295 		if (number < 0) number = 0;
1296 		if (number > 20) number = 20;
1297 		progdefaults.CWpre = number;
1298 		REQ(setPRE, number);
1299 	}
1300 }
1301 
1302 static void pTxQuePRE(std::string &s, size_t &i, size_t endbracket)
1303 {
1304 	if (within_exec) {
1305 		substitute(s, i, endbracket, "");
1306 		return;
1307 	}
1308 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doPRE };
1309 	push_txcmd(cmd);
1310 	substitute(s, i, endbracket, "^!");
1311 }
1312 
1313 static void setPOST(int d)
1314 {
1315 	cntPostTiming->value(d);
1316 	que_ok = true;
1317 }
1318 
1319 static void doPOST(std::string s)
1320 {
1321 	float number;
1322 	std::string sVal = s.substr(7, s.length() - 8);
1323 	if (sVal.length() > 0) {
1324 		sscanf(sVal.c_str(), "%f", &number);
1325 		if (number < -20) number = -20;
1326 		if (number > 20) number = 20;
1327 		progdefaults.CWpost = number;
1328 		REQ(setPOST, number);
1329 	}
1330 }
1331 
1332 static void pTxQuePOST(std::string &s, size_t &i, size_t endbracket)
1333 {
1334 	if (within_exec) {
1335 		substitute(s, i, endbracket, "");
1336 		return;
1337 	}
1338 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doPOST };
1339 	push_txcmd(cmd);
1340 	substitute(s, i, endbracket, "^!");
1341 }
1342 
1343 static void setTXATTEN(float v)
1344 {
1345 	int d = (int)(v * 10);
1346 	v = d / 10.0;
1347 	v = clamp(v, -30.0, 0.0);
1348 	progdefaults.txlevel = v;
1349 	cntTxLevel->value(progdefaults.txlevel);;
1350 	que_ok = true;
1351 }
1352 
1353 static void pTXATTEN(std::string &s, size_t &i, size_t endbracket)
1354 {
1355 	if (within_exec) {
1356 		substitute(s, i, endbracket, "");
1357 		return;
1358 	}
1359 	float number;
1360 	std::string sVal = s.substr(i+9, endbracket - i - 9);
1361 	if (sVal.length() > 0) {
1362 		sscanf(sVal.c_str(), "%f", &number);
1363 		setTXATTEN(number);
1364 	}
1365 	substitute(s, i, endbracket, "");
1366 }
1367 
1368 static void doTXATTEN(std::string s)
1369 {
1370 	float number;
1371 	std::string sVal = s.substr(10);
1372 	if (sVal.length() > 0) {
1373 		sscanf(sVal.c_str(), "%f", &number);
1374 		REQ(setTXATTEN, number);
1375 	}
1376 }
1377 
1378 static void pTxQueTXATTEN(std::string &s, size_t &i, size_t endbracket)
1379 {
1380 	if (within_exec) {
1381 		substitute(s, i, endbracket, "");
1382 		return;
1383 	}
1384 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doTXATTEN };
1385 	push_txcmd(cmd);
1386 	substitute(s, i, endbracket, "");
1387 }
1388 
1389 static void pIDLE(std::string &s, size_t &i, size_t endbracket)
1390 {
1391 	if (within_exec) {
1392 		substitute(s, i, endbracket, "");
1393 		return;
1394 	}
1395 	float number;
1396 	std::string sTime = s.substr(i+6, endbracket - i - 6);
1397 	if (sTime.length() > 0) {
1398 		sscanf(sTime.c_str(), "%f", &number);
1399 		macro_idle_on = true;
1400 		idleTime = number;
1401 	}
1402 	substitute(s, i, endbracket, "");
1403 }
1404 
1405 static int idle_time = 0; // in 0.1 second increments
1406 static int idle_count = 0;
1407 static void doneIDLE(void *)
1408 {
1409 	idle_count++;
1410 	if (idle_count == idle_time) {
1411 		Qidle_time = 0;
1412 		que_ok = true;
1413 		idle_time = idle_count = 0;
1414 		return;
1415 	}
1416 	Fl::repeat_timeout(0.1, doneIDLE);
1417 }
1418 
1419 static void doIDLE(std::string s)
1420 {
1421 	std::string sTime = s.substr(7, s.length() - 8);
1422 	if (sTime.length() > 0) {
1423 		float ftime;
1424 		if (sscanf(sTime.c_str(), "%f", &ftime) != 1)
1425 			ftime = 1.0;
1426 		idle_time = 10 * ftime;
1427 		Qidle_time = 1;
1428 		Fl::add_timeout(0.1, doneIDLE);
1429 	} else {
1430 		Qidle_time = 0;
1431 	}
1432 }
1433 
1434 static void pTxQueIDLE(std::string &s, size_t &i, size_t endbracket)
1435 {
1436 	if (within_exec) {
1437 		substitute(s, i, endbracket, "");
1438 		return;
1439 	}
1440 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doIDLE };
1441 	push_txcmd(cmd);
1442 	substitute(s, i, endbracket, "^!");
1443 }
1444 
1445 bool do_tune_on;
1446 static bool tune_on;
1447 static bool rx_tune_on;
1448 static float tune_timeout = 0;
1449 
1450 static void start_tune(void *)
1451 {
1452 	trx_tune();
1453 }
1454 
1455 static void end_tune(void *data = 0)
1456 {
1457 	if (data == 0)
1458 		trx_receive();
1459 	else
1460 		trx_transmit();
1461 	tune_on = false;
1462 }
1463 
1464 static void end_do_tune(void *)
1465 {
1466 	trx_transmit();
1467 	do_tune_on = false;
1468 }
1469 
1470 static void doTUNE(std::string s)
1471 {
1472 	std::string sTime = s.substr(7, s.length() - 8);
1473 
1474 	if (sTime.length() > 0) {
1475 		if (sscanf(sTime.c_str(), "%f", &tune_timeout) != 1)
1476 			tune_timeout = 1.0;
1477 		do_tune_on = true;
1478 		Fl::add_timeout(0, start_tune);
1479 		Fl::add_timeout(tune_timeout, end_do_tune);
1480 	}
1481 	que_ok = true;
1482 }
1483 
1484 static void pTxQueTUNE(std::string &s, size_t &i, size_t endbracket)
1485 {
1486 	if (within_exec) {
1487 		substitute(s, i, endbracket, "");
1488 		return;
1489 	}
1490 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doTUNE };
1491 	push_txcmd(cmd);
1492 	substitute(s, i, endbracket, "^!");
1493 }
1494 
1495 static void end_rxtune(void *)
1496 {
1497 	trx_receive();
1498 	rx_tune_on = false;
1499 }
1500 
1501 static void doRxTUNE(std::string s)
1502 {
1503 	std::string sTime = s.substr(7, s.length() - 8);
1504 
1505 	if (sTime.length() > 0) {
1506 		if (sscanf(sTime.c_str(), "%f", &tune_timeout) != 1)
1507 			tune_timeout = 1.0;
1508 		rx_tune_on = true;
1509 		Fl::add_timeout(0, start_tune);
1510 		Fl::add_timeout(tune_timeout, end_rxtune);
1511 	}
1512 }
1513 
1514 static void pRxQueTUNE(std::string &s, size_t &i, size_t endbracket)
1515 {
1516 	if (within_exec) {
1517 		substitute(s, i, endbracket, "");
1518 		return;
1519 	}
1520 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRxTUNE };
1521 	push_rxcmd(cmd);
1522 	substitute(s, i, endbracket, "");
1523 }
1524 
1525 static void pTUNE(std::string &s, size_t &i, size_t endbracket)
1526 {
1527 	if (within_exec) {
1528 		substitute(s, i, endbracket, "");
1529 		return;
1530 	}
1531 	std::string sTime = s.substr(i+6, endbracket - i - 6);
1532 	if (sTime.length() > 0) {
1533 		if (sscanf(sTime.c_str(), "%f", &tune_timeout) != 1)
1534 			tune_timeout = 1.0;
1535 		tune_on = true;
1536 		Fl::add_timeout(0, start_tune);
1537 	}
1538 
1539 	substitute(s, i, endbracket, "");
1540 }
1541 
1542 static void pQSONBR(std::string &s, size_t &i, size_t endbracket)
1543 {
1544 	if (within_exec) {
1545 		s.replace(i, endbracket - i + 1, "");
1546 		return;
1547 	}
1548 	char szqsonbr[10];
1549 	snprintf(szqsonbr, sizeof(szqsonbr), "%d", qsodb.nbrRecs());
1550 	s.replace(i, endbracket - i + 1, szqsonbr);
1551 }
1552 
1553 static void pNXTNBR(std::string &s, size_t &i, size_t endbracket)
1554 {
1555 	if (within_exec) {
1556 		s.replace(i, endbracket - i + 1, "");
1557 		return;
1558 	}
1559 	char szqsonbr[20];
1560 	snprintf(szqsonbr, sizeof(szqsonbr), "%d", qsodb.nbrRecs() + 1);
1561 	s.replace(i, endbracket - i + 1, szqsonbr);
1562 }
1563 
1564 static void pNRSID(std::string &s, size_t &i, size_t endbracket)
1565 {
1566 	if (within_exec) {
1567 		substitute(s, i, endbracket, "");
1568 		return;
1569 	}
1570 	int number = 0;
1571 	std::string sNumber = s.substr(i+7, endbracket - i - 7);
1572 	if (sNumber.length() > 0) {
1573 		sscanf(sNumber.c_str(), "%d", &number);
1574 		progStatus.n_rsids = number;
1575 	}
1576 	substitute(s, i, endbracket, "");
1577 }
1578 
1579 static bool useWait = false;
1580 static float  waitTime = 0;
1581 
1582 static void pWAIT(std::string &s, size_t &i, size_t endbracket)
1583 {
1584 	if (within_exec) {
1585 		substitute(s, i, endbracket, "");
1586 		return;
1587 	}
1588 	float number;
1589 	std::string sTime = s.substr(i+6, endbracket - i - 6);
1590 	if (sTime.length() > 0) {
1591 		sscanf(sTime.c_str(), "%f", &number);
1592 		useWait = true;
1593 		waitTime = number;
1594 	}
1595 	substitute(s, i, endbracket, "");
1596 }
1597 
1598 static void doneWAIT(void *)
1599 {
1600 	Qwait_time = 0;
1601 	start_tx();
1602 	que_ok = true;
1603 }
1604 
1605 static void doWAIT(std::string s)
1606 {
1607 	float number;
1608 	std::string sTime = s.substr(7, s.length() - 8);
1609 	if (sTime.length() > 0) {
1610 		sscanf(sTime.c_str(), "%f", &number);
1611 		Qwait_time = number;
1612 		Fl::add_timeout (number, doneWAIT);
1613 	} else
1614 		Qwait_time = 0;
1615 }
1616 
1617 static void pTxQueWAIT(std::string &s, size_t &i, size_t endbracket)
1618 {
1619 	if (within_exec) {
1620 		substitute(s, i, endbracket, "");
1621 		return;
1622 	}
1623 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doWAIT };
1624 	push_txcmd(cmd);
1625 	substitute(s, i, endbracket, "^!");
1626 }
1627 
1628 static void doRxWAIT(std::string s)
1629 {
1630 	float number = 0;
1631 	std::string sTime = s.substr(7, s.length() - 8);
1632 	if (sTime.length() > 0) {
1633 		sscanf(sTime.c_str(), "%f", &number);
1634 		macro_rx_wait = true;
1635 		Fl::add_timeout(number, rx_que_continue);
1636 	}
1637 }
1638 
1639 static void pRxQueWAIT(std::string &s, size_t &i, size_t endbracket)
1640 {
1641 	if (within_exec) {
1642 		substitute(s, i, endbracket, "");
1643 		return;
1644 	}
1645 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRxWAIT };
1646 	push_rxcmd(cmd);
1647 	substitute(s, i, endbracket, "");
1648 }
1649 
1650 static void pINFO1(std::string &s, size_t &i, size_t endbracket)
1651 {
1652 	s.replace( i, 7, info1msg );
1653 }
1654 
1655 static void pINFO2(std::string &s, size_t &i, size_t endbracket)
1656 {
1657 	s.replace( i, 7, info2msg );
1658 }
1659 
1660 static void pCLRRX(std::string &s, size_t &i, size_t endbracket)
1661 {
1662 	if (within_exec) {
1663 		substitute(s, i, endbracket, "");
1664 		return;
1665 	}
1666 	substitute(s, i, endbracket, "");
1667 	trx_mode md = active_modem->get_mode();
1668 
1669 	if (md == MODE_IFKP)
1670 		ifkp_rx_text->clear();
1671 	else if (md == MODE_FSQ)
1672 		fsq_rx_text->clear();
1673 	else if ((md >= MODE_FELDHELL) && (md <= MODE_HELL80))
1674 		FHdisp->clear();
1675 	else
1676 		ReceiveText->clear();
1677 }
1678 
1679 static void pCLRTX(std::string &s, size_t &i, size_t endbracket)
1680 {
1681 	if (within_exec) {
1682 		substitute(s, i, endbracket, "");
1683 		return;
1684 	}
1685 	substitute(s, i, endbracket, "");
1686 	queue_reset();
1687 	if (active_modem->get_mode() == MODE_IFKP)
1688 		ifkp_tx_text->clear();
1689 	else if (active_modem->get_mode() == MODE_FSQ)
1690 		fsq_tx_text->clear();
1691 	else
1692 		TransmitText->clear();
1693 }
1694 
1695 static void pCLRQSO(std::string &s, size_t &i, size_t endbracket)
1696 {
1697 	if (within_exec) {
1698 		substitute(s, i, endbracket, "");
1699 		return;
1700 	}
1701 	substitute(s, i, endbracket, "");
1702 	clearQSO();
1703 }
1704 
1705 static void pFOCUS(std::string &s, size_t &i, size_t endbracket)
1706 {
1707 	if (!within_exec) {
1708 		if (qsoFreqDisp->is_reversed_colors()) {
1709 			qsoFreqDisp->restore_colors();
1710 			if (active_modem->get_mode() == MODE_IFKP)
1711 				ifkp_tx_text->take_focus();
1712 			else if (active_modem->get_mode() == MODE_FSQ)
1713 				fsq_tx_text->take_focus ();
1714 			else
1715 				TransmitText->take_focus();
1716 		} else {
1717 			qsoFreqDisp->take_focus();
1718 			qsoFreqDisp->reverse_colors();
1719 		}
1720 	}
1721 	substitute(s, i, endbracket, "");
1722 }
1723 
1724 static void pQSYPLUS(std::string &s, size_t &i, size_t endbracket)
1725 {
1726 	if (within_exec) {
1727 		substitute(s, i, endbracket, "");
1728 		return;
1729 	}
1730 	int rf = 0;
1731 	float rfd = 0;
1732 	std::string sIncrFreq = s.substr(i+6, endbracket - i - 6);
1733 	// no frequency(s) specified
1734 	if (sIncrFreq.length() == 0) {
1735 		substitute(s, i, endbracket, "");
1736 		return;
1737 	}
1738 	// rf first value
1739 	sscanf(sIncrFreq.c_str(), "%f", &rfd);
1740 	if (rfd != 0) {
1741 		rf = wf->rfcarrier() + (int)(1000*rfd);
1742 		qsy(rf, active_modem ? active_modem->get_freq() : 1500);
1743 	}
1744 	substitute(s, i, endbracket, "");
1745 }
1746 
1747 static void pCALL(std::string &s, size_t &i, size_t endbracket)
1748 {
1749 	string call = inpCall->value();
1750 	if (active_modem->get_mode() == MODE_IFKP && progdefaults.ifkp_lowercase_call)
1751 		for (size_t n = 0; n < call.length(); n++) call[n] = tolower(call[n]);
1752 	s.replace( i, 6, call );
1753 }
1754 
1755 static void pGET(std::string &s, size_t &i, size_t endbracket)
1756 {
1757 	s.erase( i, 9 );
1758 	GET = true;
1759 }
1760 
1761 static void pFREQ(std::string &s, size_t &i, size_t endbracket)
1762 {
1763 	s.replace( i, 6, inpFreq->value() );
1764 }
1765 
1766 static void pBAND(std::string &s, size_t &i, size_t endbracket)
1767 {
1768         s.replace( i, 6, band_name( band( wf->rfcarrier() ) ) );
1769 }
1770 
1771 static void pLOC(std::string &s, size_t &i, size_t endbracket)
1772 {
1773 	s.replace( i, 5, inpLoc->value() );
1774 }
1775 
1776 static void pMODE(std::string &s, size_t &i, size_t endbracket)
1777 {
1778 	s.replace( i, 6, active_modem->get_mode_name());
1779 }
1780 
1781 static void pNAME(std::string &s, size_t &i, size_t endbracket)
1782 {
1783 	s.replace( i, 6, inpName->value() );
1784 }
1785 
1786 static void pQTH(std::string &s, size_t &i, size_t endbracket)
1787 {
1788 	s.replace( i,5, inpQth->value() );
1789 }
1790 
1791 static void pST(std::string &s, size_t &i, size_t endbracket)
1792 {
1793 	s.replace( i, 4, inpState->value() );
1794 }
1795 
1796 static void pPR(std::string &s, size_t &i, size_t endbracket)
1797 {
1798 	s.replace( i, 4, inpVEprov->value() );
1799 }
1800 
1801 static void pQSOTIME(std::string &s, size_t &i, size_t endbracket)
1802 {
1803 	qso_time = inpTimeOff->value();
1804 	s.replace( i, 9, qso_time.c_str() );
1805 }
1806 
1807 static void pRST(std::string &s, size_t &i, size_t endbracket)
1808 {
1809 	s.replace( i, 5, cut_string(inpRstOut->value()));
1810 }
1811 
1812 static void pMYCALL(std::string &s, size_t &i, size_t endbracket)
1813 {
1814 	string call = inpMyCallsign->value();
1815 	if (active_modem->get_mode() == MODE_IFKP && progdefaults.ifkp_lowercase)
1816 		for (size_t n = 0; n < call.length(); n++) call[n] = tolower(call[n]);
1817 	s.replace( i, 8, call );
1818 }
1819 
1820 static void pMYLOC(std::string &s, size_t &i, size_t endbracket)
1821 {
1822 	s.replace( i, 7, inpMyLocator->value() );
1823 }
1824 
1825 static void pMYNAME(std::string &s, size_t &i, size_t endbracket)
1826 {
1827 	s.replace( i, 8, inpMyName->value() );
1828 }
1829 
1830 static void pMYQTH(std::string &s, size_t &i, size_t endbracket)
1831 {
1832 	s.replace( i, 7, inpMyQth->value() );
1833 }
1834 
1835 static void pMYRST(std::string &s, size_t &i, size_t endbracket)
1836 {
1837 	s.replace( i, 7, inpRstIn->value() );
1838 }
1839 
1840 static void pANTENNA(std::string &s, size_t &i, size_t endbracket)
1841 {
1842 	s.replace( i, 9, progdefaults.myAntenna.c_str() );
1843 }
1844 
1845 static void pMYCLASS(std::string &s, size_t &i, size_t endbracket)
1846 {
1847 	s.replace( i, 9, progdefaults.my_FD_class.c_str() );
1848 }
1849 
1850 static void pMYSECTION(std::string &s, size_t &i, size_t endbracket)
1851 {
1852 	s.replace( i, 11, progdefaults.my_FD_section.c_str() );
1853 }
1854 
1855 static void pMYSTATE(std::string &s, size_t &i, size_t endbracket)
1856 {
1857 	s.replace( i, 9, listbox_states->value() );
1858 }
1859 
1860 static void pMYST(std::string &s, size_t &i, size_t endbracket)
1861 {
1862 	s.replace( i, 6,  inp_QP_state_short->value() );
1863 }
1864 
1865 static void pMYCOUNTY(std::string &s, size_t &i, size_t endbracket)
1866 {
1867 	s.replace( i, 10, listbox_counties->value() );
1868 }
1869 
1870 static void pMYCNTY(std::string &s, size_t &i, size_t endbracket)
1871 {
1872 	s.replace( i, 8, inp_QP_short_county->value() );
1873 }
1874 
1875 static void pLDT(std::string &s, size_t &i, size_t endbracket)
1876 {
1877 	char szDt[80];
1878 
1879 	std::string fmt = "%x %H:%M %Z";
1880 	std::string timefmt = s.substr(i, endbracket-i);
1881 
1882 	size_t p = timefmt.find(":");
1883 	if (p == 4) {
1884 		fmt = timefmt.substr(p + 1, timefmt.length() - p - 1);
1885 		if (fmt[0] == '"') fmt.erase(0,1);
1886 		if (fmt[fmt.length()-1] == '"') fmt.erase(fmt.length()-1);
1887 	}
1888 
1889 	time_t tmptr;
1890 	tm sTime;
1891 	time (&tmptr);
1892 	localtime_r(&tmptr, &sTime);
1893 	mystrftime(szDt, 79, fmt.c_str(), &sTime);
1894 	s.replace(i, endbracket - i + 1, szDt);
1895 }
1896 
1897 static void pILDT(std::string &s, size_t &i, size_t endbracket)
1898 {
1899 	char szDt[80];
1900 
1901 	std::string fmt = "%Y-%m-%d %H:%M%z";
1902 	std::string timefmt = s.substr(i, endbracket-i);
1903 
1904 	size_t p = timefmt.find(":");
1905 	if (p == 5) {
1906 		fmt = timefmt.substr(p + 1, timefmt.length() - p - 1);
1907 		if (fmt[0] == '"') fmt.erase(0,1);
1908 		if (fmt[fmt.length()-1] == '"') fmt.erase(fmt.length()-1);
1909 	}
1910 
1911 	time_t tmptr;
1912 	tm sTime;
1913 	time (&tmptr);
1914 	localtime_r(&tmptr, &sTime);
1915 	mystrftime(szDt, 79, fmt.c_str(), &sTime);
1916 	s.replace(i, endbracket - i + 1, szDt);
1917 }
1918 
1919 static void pZDT(std::string &s, size_t &i, size_t endbracket)
1920 {
1921 	char szDt[80];
1922 
1923 	std::string fmt = "%x %H:%MZ";
1924 	std::string timefmt = s.substr(i, endbracket - i);
1925 
1926 	size_t p = timefmt.find(":");
1927 	if (p == 4) {
1928 		fmt = timefmt.substr(p + 1, timefmt.length() - p - 1);
1929 		if (fmt[0] == '"') fmt.erase(0,1);
1930 		if (fmt[fmt.length()-1] == '"') fmt.erase(fmt.length()-1);
1931 	}
1932 
1933 	time_t tmptr;
1934 	tm sTime;
1935 	time (&tmptr);
1936 	gmtime_r(&tmptr, &sTime);
1937 	mystrftime(szDt, 79, fmt.c_str(), &sTime);
1938 	s.replace(i, endbracket - i + 1, szDt);
1939 }
1940 
1941 static void pIZDT(std::string &s, size_t &i, size_t endbracket)
1942 {
1943 	char szDt[80];
1944 
1945 	std::string fmt = "%Y-%m-%d %H:%MZ";
1946 	std::string timefmt = s.substr(i, endbracket-i);
1947 
1948 	size_t p = timefmt.find(":");
1949 	if (p == 5) {
1950 		fmt = timefmt.substr(p + 1, timefmt.length() - p - 1);
1951 		if (fmt[0] == '"') fmt.erase(0,1);
1952 		if (fmt[fmt.length()-1] == '"') fmt.erase(fmt.length()-1);
1953 	}
1954 
1955 	time_t tmptr;
1956 	tm sTime;
1957 	time (&tmptr);
1958 	gmtime_r(&tmptr, &sTime);
1959 	mystrftime(szDt, 79, fmt.c_str(), &sTime);
1960 	s.replace(i, endbracket - i + 1, szDt);
1961 }
1962 
1963 static void pLT(std::string &s, size_t &i, size_t endbracket)
1964 {
1965 	char szDt[80];
1966 
1967 	std::string fmt = "%H%ML";
1968 	std::string timefmt = s.substr(i, endbracket-i);
1969 
1970 	size_t p = timefmt.find(":");
1971 	if (p == 3) {
1972 		fmt = timefmt.substr(p + 1, timefmt.length() - p - 1);
1973 		if (fmt[0] == '"') fmt.erase(0,1);
1974 		if (fmt[fmt.length()-1] == '"') fmt.erase(fmt.length()-1);
1975 	}
1976 
1977 	time_t tmptr;
1978 	tm sTime;
1979 	time (&tmptr);
1980 	localtime_r(&tmptr, &sTime);
1981 	mystrftime(szDt, 79, fmt.c_str(), &sTime);
1982 	s.replace(i, endbracket - i + 1, szDt);
1983 }
1984 
1985 static void pZT(std::string &s, size_t &i, size_t endbracket)
1986 {
1987 	char szDt[80];
1988 
1989 	std::string fmt = "%H%MZ";
1990 	std::string timefmt = s.substr(i, endbracket-i);
1991 
1992 	size_t p = timefmt.find(":");
1993 	if (p == 3) {
1994 		fmt = timefmt.substr(p + 1, timefmt.length() - p - 1);
1995 		if (fmt[0] == '"') fmt.erase(0,1);
1996 		if (fmt[fmt.length()-1] == '"') fmt.erase(fmt.length()-1);
1997 	}
1998 
1999 	time_t tmptr;
2000 	tm sTime;
2001 	time (&tmptr);
2002 	gmtime_r(&tmptr, &sTime);
2003 	mystrftime(szDt, 79, fmt.c_str(), &sTime);
2004 	s.replace(i, endbracket - i + 1, szDt);
2005 }
2006 
2007 static void pLD(std::string &s, size_t &i, size_t endbracket)
2008 {
2009 	char szDt[80];
2010 
2011 	std::string fmt = "%Y-%m-%d";
2012 	std::string timefmt = s.substr(i, endbracket - i);
2013 
2014 	size_t p = timefmt.find(":");
2015 	if (p == 3) {
2016 		fmt = timefmt.substr(p + 1, timefmt.length() - p - 1);
2017 		if (fmt[0] == '"') fmt.erase(0,1);
2018 		if (fmt[fmt.length()-1] == '"') fmt.erase(fmt.length()-1);
2019 	}
2020 
2021 	time_t tmptr;
2022 	tm sTime;
2023 	time (&tmptr);
2024 	localtime_r(&tmptr, &sTime);
2025 	mystrftime(szDt, 79, fmt.c_str(), &sTime);
2026 	s.replace(i, endbracket - i + 1, szDt);
2027 }
2028 
2029 static void pZD(std::string &s, size_t &i, size_t endbracket)
2030 {
2031 	char szDt[80];
2032 
2033 	std::string fmt = "%Y-%m-%d";
2034 	std::string timefmt = s.substr(i, endbracket - i);
2035 
2036 	size_t p = timefmt.find(":");
2037 	if (p == 3) {
2038 		fmt = timefmt.substr(p + 1, timefmt.length() - p - 1);
2039 		if (fmt[0] == '"') fmt.erase(0,1);
2040 		if (fmt[fmt.length()-1] == '"') fmt.erase(fmt.length()-1);
2041 	}
2042 
2043 	time_t tmptr;
2044 	tm sTime;
2045 	time (&tmptr);
2046 	gmtime_r(&tmptr, &sTime);
2047 	mystrftime(szDt, 79, fmt.c_str(), &sTime);
2048 	s.replace(i, endbracket - i + 1, szDt);
2049 }
2050 
2051 static void p_ID(std::string &s, size_t &i, size_t endbracket)
2052 {
2053 	if (within_exec) {
2054 		substitute(s, i, endbracket, "");
2055 		return;
2056 	}
2057 	progdefaults.macroid = true;
2058 	substitute(s, i, endbracket, "");
2059 }
2060 
2061 static void pTEXT(std::string &s, size_t &i, size_t endbracket)
2062 {
2063 	if (within_exec) {
2064 		substitute(s, i, endbracket, "");
2065 		return;
2066 	}
2067 	progdefaults.macrotextid = true;
2068 	substitute(s, i, endbracket, "");
2069 }
2070 
2071 static void doCWID(std::string s)
2072 {
2073 	progdefaults.macroCWid = true;
2074 	que_ok = true;
2075 }
2076 
2077 static void pCWID(std::string &s, size_t &i, size_t endbracket)
2078 {
2079 	if (within_exec) {
2080 		substitute(s, i, endbracket, "");
2081 		return;
2082 	}
2083 	CMDS cmd = {s.substr(i, endbracket - i + 1), doCWID};
2084 	push_txcmd(cmd);
2085 	substitute(s, i, endbracket, "^!");
2086 }
2087 
2088 static void doDTMF(std::string s)
2089 {
2090 	progdefaults.DTMFstr = s.substr(6, s.length() - 8);
2091 	que_ok = true;
2092 }
2093 
2094 static void pDTMF(std::string &s, size_t &i, size_t endbracket)
2095 {
2096 	if (within_exec) {
2097 		substitute(s, i, endbracket, "");
2098 		return;
2099 	}
2100 	CMDS cmd = {s.substr(i, endbracket - i + 1), doDTMF};
2101 	push_txcmd(cmd);
2102 	substitute(s, i, endbracket, "^!");
2103 }
2104 
2105 static void pALERT(std::string &s, size_t &i, size_t endbracket)
2106 {
2107 	if (trx_state != STATE_RX) {
2108 		substitute(s, i, endbracket, "");
2109 		return;
2110 	}
2111 
2112 	if (within_exec) {
2113 		substitute(s, i, endbracket, "");
2114 		return;
2115 	}
2116 	std::string cmd = s.substr(i+7, endbracket - i - 7);
2117 	if (audio_alert)
2118 		try {
2119 			audio_alert->alert(cmd);
2120 		} catch (...) {
2121 		}
2122 	substitute(s, i, endbracket, "");
2123 }
2124 
2125 static void doAUDIO(std::string s)
2126 {
2127 	s.erase(0, 16);
2128 	active_modem->Audio_filename(s);
2129 	que_ok = true;
2130 }
2131 
2132 static void pAUDIO(std::string &s, size_t &i, size_t endbracket)
2133 {
2134 	if (within_exec) {
2135 		substitute(s, i, endbracket, "");
2136 		return;
2137 	}
2138 	std::string acmd = "Xmt audio file: ";
2139 	acmd.append(s.substr(i + 7, endbracket - (i + 7)));
2140 	CMDS cmd = {acmd, doAUDIO};
2141 	push_txcmd(cmd);
2142 	substitute(s, i, endbracket, "^!");
2143 }
2144 
2145 static void pPAUSE(std::string &s, size_t &i, size_t endbracket)
2146 {
2147 	if (within_exec) {
2148 		substitute(s, i, endbracket, "");
2149 		return;
2150 	}
2151 	substitute(s, i, endbracket, "");
2152 }
2153 
2154 static void pRX(std::string &s, size_t &i, size_t endbracket)
2155 {
2156 	if (within_exec) {
2157 		substitute(s, i, endbracket, "");
2158 		return;
2159 	}
2160 	substitute(s, i, endbracket, "^r");
2161 }
2162 
2163 static void pTX(std::string &s, size_t &i, size_t endbracket)
2164 {
2165 	if (within_exec) {
2166 		substitute(s, i, endbracket, "");
2167 		return;
2168 	}
2169 	substitute(s, i, endbracket, "");
2170 	if (rx_only)
2171 		TransmitON = false;
2172 	else {
2173 		start_macro_time();
2174 		TransmitON = true;
2175 	}
2176 }
2177 
2178 static void pTXRX(std::string &s, size_t &i, size_t endbracket)
2179 {
2180 	if (within_exec) {
2181 		substitute(s, i, endbracket, "");
2182 		return;
2183 	}
2184 	substitute(s, i, endbracket, "");
2185 	if (rx_only)
2186 		ToggleTXRX = false;
2187 	else
2188 		ToggleTXRX = true;
2189 }
2190 
2191 /*
2192 static std::string hexstr(std::string &s)
2193 {
2194 	static std::string hex;
2195 	static char val[3];
2196 	hex.clear();
2197 	for (size_t i = 0; i < s.length(); i++) {
2198 		snprintf(val, sizeof(val), "%02x", s[i] & 0xFF);
2199 		hex.append("<").append(val).append(">");
2200 	}
2201 	return hex;
2202 }
2203 */
2204 
2205 static void doRIGCAT(std::string s)
2206 {
2207 	size_t start = s.find(':');
2208 	std::string buff;
2209 
2210 	LOG_INFO("!RIGCAT %s", s.substr(start + 1, s.length() - start + 1).c_str());
2211 
2212 	size_t val = 0;
2213 	int retnbr = 0;
2214 	char c, ch;
2215 	bool asciisw = false;
2216 	bool valsw = false;
2217 
2218 	for (size_t j = start+1 ; j <= s.length() ; j++) {
2219 		ch = s[j];
2220 		if (ch == '\"') {
2221 			asciisw = !asciisw;
2222 			continue;
2223 		}
2224 		// accumulate ascii string
2225 		if (asciisw) {
2226 			if (isprint(ch)) buff += ch;
2227 			continue;
2228 		}
2229 		// following digits is expected size of CAT response from xcvr
2230 		if (ch == ':' && s[j+1] != '>') {
2231 			sscanf(&s[j+1], "%d", &retnbr);
2232 		}
2233 		// accumulate hex string values
2234 		if ((ch == ' ' || ch == '>' || ch == ':') && valsw) {
2235 			c = char(val);
2236 			//			LOG_INFO("c=%02x, val=%d", c, val);
2237 			buff += c;
2238 			val = 0;
2239 			valsw = false;
2240 		} else {
2241 			val *= 16;
2242 			ch = toupper(ch);
2243 			if (isdigit(ch)) val += ch - '0';
2244 			else if (ch >= 'A' && ch <= 'F') val += ch - 'A' + 10;
2245 			valsw = true;
2246 		}
2247 		if (ch == ':') break;
2248 	}
2249 
2250 	add_to_cmdque("RIGCAT macro", buff, retnbr, progdefaults.RigCatWait);
2251 
2252 	que_ok = true;
2253 }
2254 
2255 static void pTxQueRIGCAT(std::string &s, size_t &i, size_t endbracket)
2256 {
2257 	if (within_exec) {
2258 		substitute(s, i, endbracket, "");
2259 		return;
2260 	}
2261 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRIGCAT };
2262 	push_txcmd(cmd);
2263 	substitute(s, i, endbracket, "^!");
2264 }
2265 
2266 static void pRxQueRIGCAT(std::string &s, size_t &i, size_t endbracket)
2267 {
2268 	if (within_exec) {
2269 		substitute(s, i, endbracket, "");
2270 		return;
2271 	}
2272 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRIGCAT };
2273 	push_rxcmd(cmd);
2274 	substitute(s, i, endbracket, "");
2275 }
2276 
2277 static void pRIGCAT(std::string &s, size_t &i, size_t endbracket)
2278 {
2279 	if (within_exec) {
2280 		substitute(s, i, endbracket, "");
2281 		return;
2282 	}
2283 
2284 	LOG_INFO("cat cmd:retnbr %s", s.substr(i, endbracket - i + 1).c_str());
2285 
2286 	size_t start = s.find(':', i);
2287 	std::basic_string<char> buff;
2288 
2289 	size_t val = 0;
2290 	int retnbr = 0;
2291 	char c, ch;
2292 	bool asciisw = false;
2293 	bool valsw = false;
2294 
2295 	for (size_t j = start+1 ; j <= endbracket ; j++) {
2296 		ch = s[j];
2297 		if (ch == '\"') {
2298 			asciisw = !asciisw;
2299 			continue;
2300 		}
2301 		// accumulate ascii string
2302 		if (asciisw) {
2303 			if (isprint(ch)) buff += ch;
2304 			continue;
2305 		}
2306 		// following digits is expected size of CAT response from xcvr
2307 		if (ch == ':' && s[j+1] != '>') {
2308 			sscanf(&s[j+1], "%d", &retnbr);
2309 		}
2310 		// accumulate hex string values
2311 		if ((ch == ' ' || ch == '>' || ch == ':') && valsw) {
2312 			c = char(val);
2313 			//			LOG_INFO("c=%02x, val=%d", c, val);
2314 			buff += c;
2315 			val = 0;
2316 			valsw = false;
2317 		} else {
2318 			val *= 16;
2319 			ch = toupper(ch);
2320 			if (isdigit(ch)) val += ch - '0';
2321 			else if (ch >= 'A' && ch <= 'F') val += ch - 'A' + 10;
2322 			valsw = true;
2323 		}
2324 		if (ch == ':') break;
2325 	}
2326 
2327 	add_to_cmdque( "RIGCAT macro", buff, retnbr, progdefaults.RigCatWait);
2328 
2329 	substitute(s, i, endbracket, "");
2330 }
2331 
2332 static void doFLRIG(std::string s)
2333 {
2334 	size_t start = s.find(':');
2335 	std::string cmd = s.substr(start + 1, s.length() - start + 1);
2336 
2337 	LOG_INFO("!FLRIG %s", cmd.c_str());
2338 
2339 	xmlrpc_send_command(cmd);
2340 
2341 	que_ok = true;
2342 }
2343 
2344 static void pTxQueFLRIG(std::string &s, size_t &i, size_t endbracket)
2345 {
2346 	if (within_exec) {
2347 		substitute(s, i, endbracket, "");
2348 		return;
2349 	}
2350 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doFLRIG };
2351 	push_txcmd(cmd);
2352 	substitute(s, i, endbracket, "^!");
2353 }
2354 
2355 static void pRxQueFLRIG(std::string &s, size_t &i, size_t endbracket)
2356 {
2357 	if (within_exec) {
2358 		substitute(s, i, endbracket, "");
2359 		return;
2360 	}
2361 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doFLRIG };
2362 	push_rxcmd(cmd);
2363 	substitute(s, i, endbracket, "");
2364 }
2365 
2366 static void pFLRIG(std::string &s, size_t &i, size_t endbracket)
2367 {
2368 	if (within_exec) {
2369 		substitute(s, i, endbracket, "");
2370 		return;
2371 	}
2372 
2373 	LOG_INFO("flrig CAT cmd: %s", s.substr(i, endbracket - i + 1).c_str());
2374 
2375 	size_t start = s.find(':');
2376 	std::string cmd = s.substr(start + 1, s.length() - start - 2);
2377 	xmlrpc_send_command(cmd);
2378 
2379 	substitute(s, i, endbracket, "");
2380 }
2381 
2382 static void doVIDEO(string s)
2383 {
2384 	trx_mode id = active_modem->get_mode();
2385 	if ( id == MODE_SSB ||
2386 		id == MODE_ANALYSIS ||
2387 		id == MODE_WEFAX_576 || id == MODE_WEFAX_288 ||
2388 		id == MODE_SITORB || id == MODE_NAVTEX ) {
2389 		return;
2390 	}
2391 	size_t start = s.find(':') + 1;
2392 	size_t end = s.find('>');
2393 	active_modem->macro_video_text = s.substr(start, end - start);
2394 	que_ok = true;
2395 }
2396 
2397 static void pVIDEO(std::string &s, size_t &i, size_t endbracket)
2398 {
2399 	if (within_exec) {
2400 		substitute(s, i, endbracket, "");
2401 		return;
2402 	}
2403 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doVIDEO };
2404 	push_txcmd(cmd);
2405 	substitute(s, i, endbracket, "^!");
2406 }
2407 
2408 
2409 static void pVER(std::string &s, size_t &i, size_t endbracket)
2410 {
2411 	std::string progname;
2412 	progname = "Fldigi ";
2413 	progname.append(PACKAGE_VERSION);
2414 	s.replace( i, 5, progname );
2415 }
2416 
2417 static void pSERNO(std::string &s, size_t &i, size_t endbracket)
2418 {
2419 	if (within_exec) {
2420 		s.replace(i, endbracket - i + 1, "");
2421 		return;
2422 	}
2423 	int  contestval;
2424 	contestval = atoi(outSerNo->value());
2425 	if (contestval) {
2426 		char serstr[10];
2427 		contest_count.Format(progdefaults.ContestDigits, progdefaults.UseLeadingZeros);
2428 		snprintf(serstr, sizeof(serstr), contest_count.fmt.c_str(), contestval);
2429 		s.replace (i, 7, cut_string(serstr));
2430 	} else
2431 		s.replace (i, 7, "");
2432 }
2433 
2434 static void pLASTNO(std::string &s, size_t &i, size_t endbracket)
2435 {
2436 	if (within_exec) {
2437 		s.replace(i, endbracket - i + 1, "");
2438 		return;
2439 	}
2440 	int  contestval;
2441 	contestval = atoi(outSerNo->value()) - 1;
2442 	if (contestval) {
2443 		char serstr[10];
2444 		contest_count.Format(progdefaults.ContestDigits, progdefaults.UseLeadingZeros);
2445 		snprintf(serstr, sizeof(serstr), contest_count.fmt.c_str(), contestval);
2446 		s.replace (i, 8, cut_string(serstr));
2447 	} else
2448 		s.replace (i, 8, "");
2449 }
2450 
2451 static void pCNTR(std::string &s, size_t &i, size_t endbracket)
2452 {
2453 	if (within_exec) {
2454 		s.replace(i, endbracket - i + 1, "");
2455 		return;
2456 	}
2457 	int  contestval;
2458 	contestval = contest_count.count;
2459 	if (contestval) {
2460 		contest_count.Format(progdefaults.ContestDigits, progdefaults.UseLeadingZeros);
2461 		snprintf(contest_count.szCount, sizeof(contest_count.szCount), contest_count.fmt.c_str(), contestval);
2462 		s.replace (i, 6, cut_string(contest_count.szCount));
2463 	} else
2464 		s.replace (i, 6, "");
2465 }
2466 
2467 static void pDECR(std::string &s, size_t &i, size_t endbracket)
2468 {
2469 	if (within_exec) {
2470 		substitute(s, i, endbracket, "");
2471 		return;
2472 	}
2473 	contest_count.count--;
2474 	if (contest_count.count < 0) contest_count.count = 0;
2475 	substitute(s, i, endbracket, "");
2476 	updateOutSerNo();
2477 }
2478 
2479 static void pINCR(std::string &s, size_t &i, size_t endbracket)
2480 {
2481 	if (within_exec) {
2482 		substitute(s, i, endbracket, "");
2483 		return;
2484 	}
2485 	contest_count.count++;
2486 	substitute(s, i, endbracket, "");
2487 	updateOutSerNo();
2488 }
2489 
2490 static void pXIN(std::string &s, size_t &i, size_t endbracket)
2491 {
2492 	s.replace( i, 5, inpXchgIn->value() );
2493 }
2494 
2495 static void pXOUT(std::string &s, size_t &i, size_t endbracket)
2496 {
2497 	s.replace( i, 6, cut_string(progdefaults.myXchg.c_str()));
2498 }
2499 
2500 static void pXBEG(std::string &s, size_t &i, size_t endbracket)
2501 {
2502 	if (within_exec) {
2503 		substitute(s, i, endbracket, "");
2504 		return;
2505 	}
2506 	substitute(s, i, endbracket, "");
2507 	xbeg = i;
2508 }
2509 
2510 static void pXEND(std::string &s, size_t &i, size_t endbracket)
2511 {
2512 	if (within_exec) {
2513 		substitute(s, i, endbracket, "");
2514 		return;
2515 	}
2516 	substitute(s, i, endbracket, "");
2517 	xend = i;
2518 }
2519 
2520 static void pSAVEXCHG(std::string &s, size_t &i, size_t endbracket)
2521 {
2522 	if (within_exec) {
2523 		substitute(s, i, endbracket, "");
2524 		return;
2525 	}
2526 	save_xchg = true;
2527 	substitute(s, i, endbracket, "");
2528 }
2529 
2530 static void pFD_CLASS(std::string &s, size_t &i, size_t endbracket)
2531 {
2532 	s.replace( i, 9, inpClass->value() );
2533 }
2534 
2535 static void pFD_SECTION(std::string &s, size_t &i, size_t endbracket)
2536 {
2537 	s.replace( i, 8, inpSection->value() );
2538 }
2539 
2540 static void pCLASS(std::string &s, size_t &i, size_t endbracket)
2541 {
2542 	s.replace( i, 7, inpClass->value() );
2543 }
2544 
2545 static void pSECTION(std::string &s, size_t &i, size_t endbracket)
2546 {
2547 	s.replace( i, 9, inpSection->value() );
2548 }
2549 
2550 static void pLOG(std::string &s, size_t &i, size_t endbracket)
2551 {
2552 	if (within_exec) {
2553 		substitute(s, i, endbracket, "");
2554 		return;
2555 	}
2556 	size_t start = s.find(':', i);
2557 	if (start != std::string::npos) {
2558 		string msg = inpNotes->value();
2559 		if (!msg.empty()) msg.append("\n");
2560 		msg.append(s.substr(start + 1, endbracket-start-1));
2561 		inpNotes->value(msg.c_str());
2562 	}
2563 	substitute(s, i, endbracket, "");
2564 	qsoSave_cb(0, 0);
2565 }
2566 
2567 static void pLNW(std::string &s, size_t &i, size_t endbracket)
2568 {
2569 	if (within_exec) {
2570 	substitute(s, i, endbracket, "");
2571 		return;
2572 	}
2573 	size_t start = s.find(':', i);
2574 	if (start != std::string::npos) {
2575 		string msg = inpNotes->value();
2576 		if (!msg.empty()) msg.append("\n");
2577 		msg.append(s.substr(start + 1, endbracket-start-1));
2578 		inpNotes->value(msg.c_str());
2579 	}
2580 	substitute(s, i, endbracket, "");
2581 	qsoSave_cb(0, 0);
2582 }
2583 
2584 static void pCLRLOG(std::string &s, size_t &i, size_t endbracket)
2585 {
2586 	if (within_exec) {
2587 		substitute(s, i, endbracket, "");
2588 		return;
2589 	}
2590 	substitute(s, i, endbracket, "");
2591 }
2592 
2593 static void pMODEM_COMPSKED(std::string &s, size_t &i, size_t endbracket)
2594 {
2595 	if (within_exec) {
2596 		substitute(s, i, endbracket, "");
2597 		return;
2598 	}
2599 	size_t	j, k,
2600 	len = s.length();
2601 	std::string name;
2602 
2603 	if ((j = s.find('>', i)) == std::string::npos)
2604 		return;
2605 	while (++j < len)
2606 		if (!isspace(s[j])) break;
2607 	k = j;
2608 	while (++k < len)
2609 		if (isspace(s[k])  || s[k] == '<') break;
2610 	name = ucasestr(s.substr(j, k - j));
2611 	for (int m = 0; m < NUM_MODES; m++) {
2612 		if (name == ucasestr(mode_info[m].sname)) {
2613 			if (active_modem->get_mode() != mode_info[m].mode)
2614 				init_modem(mode_info[m].mode);
2615 			break;
2616 		}
2617 	}
2618 	s.erase(i, k-i);
2619 }
2620 
2621 static void doIMAGE(std::string s)
2622 {
2623 	if (s.length() > 0) {
2624 
2625 		bool Greyscale = false;
2626 		size_t p = string::npos;
2627 		string fname = s.substr(7);
2628 		p = fname.find(">");
2629 		fname.erase(p);
2630 		p = fname.find("G,");
2631 		if (p == string::npos) p = fname.find("g,");
2632 		if (p != string::npos) {
2633 			Greyscale = true;
2634 			fname.erase(p,2);
2635 		}
2636 		while (fname[0] == ' ') fname.erase(0,1);
2637 
2638 		trx_mode active_mode = active_modem->get_mode();
2639 		if ((active_mode == MODE_MFSK16 ||
2640 			 active_mode == MODE_MFSK32 ||
2641 			 active_mode == MODE_MFSK64 ||
2642 			 active_mode == MODE_MFSK128) &&
2643 			 active_modem->get_cap() & modem::CAP_IMG) {
2644 			Greyscale ?
2645 				active_modem->send_Grey_image(fname) :
2646 				active_modem->send_color_image(fname);
2647 		} else if (active_mode >= MODE_THOR_FIRST && active_mode <= MODE_THOR_LAST) {
2648 			thor_load_scaled_image(fname, Greyscale);
2649         } else if (active_mode == MODE_IFKP) {
2650 			ifkp_load_scaled_image(fname, Greyscale);
2651 		}
2652 	}
2653 	que_ok = true;
2654 }
2655 
2656 static void pTxQueIMAGE(std::string &s, size_t &i, size_t endbracket)
2657 {
2658 	if (within_exec) {
2659 		substitute(s, i, endbracket, "");
2660 		return;
2661 	}
2662 	string Tx_cmdstr = s.substr(i, endbracket - i + 1);
2663 	struct CMDS cmd = { Tx_cmdstr, doIMAGE };
2664 	push_txcmd(cmd);
2665 	substitute(s, i, endbracket, "^!");
2666 }
2667 
2668 static void doINSERTIMAGE(std::string s)
2669 {
2670 	if (s.length() > 0) {
2671 
2672 		bool Greyscale = false;
2673 		size_t p = string::npos;
2674 		string fname = s.substr(7);
2675 		p = fname.find(">");
2676 		fname.erase(p);
2677 		p = fname.find("G,");
2678 		if (p == string::npos) p = fname.find("g,");
2679 		if (p != string::npos) {
2680 			Greyscale = true;
2681 			fname.erase(p,2);
2682 		}
2683 		while (fname[0] == ' ') fname.erase(0,1);
2684 		if (s.empty()) return;
2685 
2686 		trx_mode md = active_modem->get_mode();
2687 		if ((md == MODE_MFSK16 || md == MODE_MFSK32 ||
2688 			 md == MODE_MFSK64 || md == MODE_MFSK128) &&
2689 			active_modem->get_cap() & modem::CAP_IMG) {
2690 				Greyscale ?
2691 					active_modem->send_Grey_image(fname) :
2692 					active_modem->send_color_image(fname);
2693 		}
2694 		else if (md == MODE_IFKP)
2695 			ifkp_load_scaled_image(fname, Greyscale);
2696 		else if (md >= MODE_THOR_FIRST && md <= MODE_THOR_LAST)
2697 			thor_load_scaled_image(fname, Greyscale);
2698 	}
2699 	que_ok = true;
2700 }
2701 
2702 void TxQueINSERTIMAGE(std::string s)
2703 {
2704 	trx_mode active_mode = active_modem->get_mode();
2705 	if (! (active_mode == MODE_MFSK16 ||
2706 		   active_mode == MODE_MFSK32 ||
2707 		   active_mode == MODE_MFSK64 ||
2708 		   active_mode == MODE_MFSK128 ||
2709 		   active_mode == MODE_IFKP ||
2710 		   (active_mode >= MODE_THOR_FIRST && active_mode <= MODE_THOR_LAST) ) &&
2711 		   active_modem->get_cap() & modem::CAP_IMG)
2712 		return;
2713 
2714 	string scmd = "<IMAGE:>";
2715 	scmd.insert(7,s);
2716 
2717 	struct CMDS cmd = { scmd, doINSERTIMAGE };
2718 	push_txcmd(cmd);
2719 
2720 	string itext = s;
2721 	size_t p = itext.rfind("\\");
2722 	if (p == string::npos) p = itext.rfind("/");
2723 	if (p != string::npos) itext.erase(0, p+1);
2724 	p = itext.rfind(".");
2725 	if (p != string::npos) itext.erase(p);
2726 	itext.insert(0, "\nImage: ");
2727 	itext.append(" ^!");
2728 
2729 	if (active_mode == MODE_IFKP)
2730 		ifkp_tx_text->add_text(itext);
2731 	else if (active_mode == MODE_FSQ)
2732 		fsq_tx_text->add_text(itext);
2733 	else
2734 		add_text(itext);
2735 }
2736 
2737 static void doAVATAR(std::string s)
2738 {
2739 	if (active_modem->get_mode() == MODE_IFKP)
2740 		active_modem->m_ifkp_send_avatar();
2741 
2742 	que_ok = true;
2743 }
2744 
2745 static void pTxQueAVATAR(std::string &s, size_t &i, size_t endbracket)
2746 {
2747 	if (within_exec) {
2748 		s.replace(i, endbracket - i + 1, "");
2749 		return;
2750 	}
2751 
2752 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doAVATAR };
2753 	push_txcmd(cmd);
2754 	substitute(s, i, endbracket, "^!");
2755 }
2756 
2757 static void doMODEM(std::string s)
2758 {
2759 	static fre_t re("<!MODEM:([[:alnum:]-]+)((:[[:digit:].+-]*)*)>", REG_EXTENDED);
2760 	std::string tomatch = s;
2761 	for (int i = 2; i < 7; i++) s[i] = toupper(s[i]);
2762 
2763 	if (!re.match(tomatch.c_str())) {
2764 		que_ok = true;
2765 		return;
2766 	}
2767 
2768 	const std::vector<regmatch_t>& o = re.suboff();
2769 	std::string name = ucasestr(tomatch.substr(o[1].rm_so, o[1].rm_eo - o[1].rm_so));
2770 
2771 	trx_mode m;
2772 	for (m = 0; m < NUM_MODES; m++)
2773 		if (name == ucasestr(mode_info[m].sname))
2774 			break;
2775 	// do we have arguments and a valid modem?
2776 	if (o.size() == 2 || m == NUM_MODES) {
2777 		que_ok = true;
2778 		return;
2779 	}
2780 
2781 	// parse arguments
2782 	vector<double> args;
2783 	args.reserve(8);
2784 	char* end;
2785 	double d;
2786 	for (const char* p = s.c_str() + o[2].rm_so + 1; *p; p++) {
2787 		errno = 0;
2788 		d = strtod(p, &end);
2789 		if (!errno && p != end) {
2790 			args.push_back(d);
2791 			p = end;
2792 		}
2793 		else // push an invalid value
2794 			args.push_back(DBL_MIN);
2795 	}
2796 
2797 	try {
2798 		switch (m) {
2799 			case MODE_RTTY: // carrier shift, baud rate, bits per char
2800 				if (args.at(0) != DBL_MIN)
2801 					set_rtty_shift((int)args[0]);
2802 				if (args.at(1) != DBL_MIN)
2803 					set_rtty_baud((float)args[1]);
2804 				if (args.at(2) != DBL_MIN)
2805 					set_rtty_bits((int)args[2]);
2806 				break;
2807 			case MODE_CONTESTIA: // bandwidth, tones
2808 				if (args.at(0) != DBL_MIN && args.at(1) != DBL_MIN) {
2809 					int bw = (int)args[0];
2810 					int tones = (int)args[1];
2811 					set_contestia_bw(bw);
2812 					set_contestia_tones(tones);
2813 					switch (tones) {
2814 						case 4 :
2815 							if (bw == 125) m = MODE_CONTESTIA_4_125;
2816 							else if (bw == 250) m = MODE_CONTESTIA_4_250;
2817 							else if (bw == 500) m = MODE_CONTESTIA_4_500;
2818 							else if (bw == 1000) m = MODE_CONTESTIA_4_1000;
2819 							else if (bw == 2000) m = MODE_CONTESTIA_4_2000;
2820 							else {
2821 								set_contestia_bw(bw);
2822 								set_contestia_tones(tones);
2823 							}
2824 							break;
2825 						case 8 :
2826 							if (bw == 125) m = MODE_CONTESTIA_8_125;
2827 							else if (bw == 250) m = MODE_CONTESTIA_8_250;
2828 							else if (bw == 500) m = MODE_CONTESTIA_8_500;
2829 							else if (bw == 1000) m = MODE_CONTESTIA_8_1000;
2830 							else if (bw == 2000) m = MODE_CONTESTIA_8_2000;
2831 							else {
2832 								set_contestia_bw(bw);
2833 								set_contestia_tones(tones);
2834 							}
2835 							break;
2836 						case 16 :
2837 							if (bw == 250) m = MODE_CONTESTIA_16_250;
2838 							else if (bw == 500) m = MODE_CONTESTIA_16_500;
2839 							else if (bw == 1000) m = MODE_CONTESTIA_16_1000;
2840 							else if (bw == 2000) m = MODE_CONTESTIA_16_2000;
2841 							else {
2842 								set_contestia_bw(bw);
2843 								set_contestia_tones(tones);
2844 							}
2845 							break;
2846 						case 32 :
2847 							if (bw == 1000) m = MODE_CONTESTIA_32_1000;
2848 							else if (bw == 2000) m = MODE_CONTESTIA_32_2000;
2849 							else {
2850 								set_contestia_bw(bw);
2851 								set_contestia_tones(tones);
2852 							}
2853 							break;
2854 						case 64 :
2855 							if (bw == 500) m = MODE_CONTESTIA_64_500;
2856 							else if (bw == 1000) m = MODE_CONTESTIA_64_1000;
2857 							else if (bw == 2000) m = MODE_CONTESTIA_64_2000;
2858 							else {
2859 								set_contestia_bw(bw);
2860 								set_contestia_tones(tones);
2861 							}
2862 							break;
2863 						default :
2864 							set_contestia_bw(bw);
2865 							set_contestia_tones(tones);
2866 					}
2867 				}
2868 				break;
2869 			case MODE_OLIVIA: // bandwidth, tones
2870 				if (args.at(0) != DBL_MIN && args.at(1) != DBL_MIN) {
2871 					int bw = (int)args[0];
2872 					int tones = (int)args[1];
2873 					set_olivia_bw(bw);
2874 					set_olivia_tones(tones);
2875 					switch (tones) {
2876 						case 4 :
2877 							if (bw == 125) m = MODE_OLIVIA_4_125;
2878 							else if (bw == 250) m = MODE_OLIVIA_4_250;
2879 							else if (bw == 500) m = MODE_OLIVIA_4_500;
2880 							else if (bw == 1000) m = MODE_OLIVIA_4_1000;
2881 							else if (bw == 2000) m = MODE_OLIVIA_4_2000;
2882 							else  {
2883 								set_olivia_bw(bw);
2884 								set_olivia_tones(tones);
2885 							}
2886 							break;
2887 						case 8 :
2888 							if (bw == 125) m = MODE_OLIVIA_8_125;
2889 							else if (bw == 250) m = MODE_OLIVIA_8_250;
2890 							else if (bw == 500) m = MODE_OLIVIA_8_500;
2891 							else if (bw == 1000) m = MODE_OLIVIA_8_1000;
2892 							else if (bw == 2000) m = MODE_OLIVIA_8_2000;
2893 							else  {
2894 								set_olivia_bw(bw);
2895 								set_olivia_tones(tones);
2896 							}
2897 							break;
2898 						case 16 :
2899 							if (bw == 500) m = MODE_OLIVIA_16_500;
2900 							else if (bw == 1000) m = MODE_OLIVIA_16_1000;
2901 							else if (bw == 2000) m = MODE_OLIVIA_16_2000;
2902 							else  {
2903 								set_olivia_bw(bw);
2904 								set_olivia_tones(tones);
2905 							}
2906 							break;
2907 						case 32 :
2908 							if (bw == 1000) m = MODE_OLIVIA_32_1000;
2909 							else if (bw == 2000) m = MODE_OLIVIA_32_2000;
2910 							else  {
2911 								set_olivia_bw(bw);
2912 								set_olivia_tones(tones);
2913 							}
2914 							break;
2915 						case 64 :
2916 							if (bw == 500) m = MODE_OLIVIA_64_500;
2917 							else if (bw == 1000) m = MODE_OLIVIA_64_1000;
2918 							else if (bw == 2000) m = MODE_OLIVIA_64_2000;
2919 							else  {
2920 								set_olivia_bw(bw);
2921 								set_olivia_tones(tones);
2922 							}
2923 							break;
2924 						default :
2925 							set_olivia_bw(bw);
2926 							set_olivia_tones(tones);
2927 					}
2928 				}
2929 				break;
2930 			default:
2931 				break;
2932 		}
2933 	}
2934 	catch (const exception& e) { }
2935 
2936 	if (active_modem->get_mode() != mode_info[m].mode) {
2937 		init_modem_sync(mode_info[m].mode);
2938 	}
2939 	que_ok = true;
2940 }
2941 
2942 static void pTxQueMODEM(std::string &s, size_t &i, size_t endbracket)
2943 {
2944 	if (within_exec) {
2945 		substitute(s, i, endbracket, "");
2946 		return;
2947 	}
2948 	string Tx_cmdstr = s.substr(i, endbracket - i + 1);
2949 	struct CMDS cmd = { Tx_cmdstr, doMODEM };
2950 	if (Tx_cmdstr.find("SSB") != string::npos || Tx_cmdstr.find("ANALYSIS") != string::npos) {
2951 		LOG_ERROR("Disallowed: %s", Tx_cmdstr.c_str());
2952 		size_t nowbracket = s.find('<', endbracket);
2953 		if (nowbracket != string::npos)
2954 			s.erase(i, nowbracket - i - 1);
2955 		else
2956 			s.clear();
2957 	} else {
2958 		push_txcmd(cmd);
2959 		if (i && s[i-1] == '\n') i--;
2960 		substitute(s, i, endbracket, "^!");
2961 	}
2962 }
2963 
2964 static void pRxQueMODEM(std::string &s, size_t &i, size_t endbracket)
2965 {
2966 	if (within_exec) {
2967 		substitute(s, i, endbracket, "");
2968 		return;
2969 	}
2970 	string rx_cmdstr = s.substr(i, endbracket - i + 1);
2971 	struct CMDS cmd = { rx_cmdstr, doMODEM };
2972 	push_rxcmd(cmd);
2973 	substitute(s, i, endbracket, "");
2974 }
2975 
2976 static void pMODEM(std::string &s, size_t &i, size_t endbracket)
2977 {
2978 	if (within_exec) {
2979 		substitute(s, i, endbracket, "");
2980 		return;
2981 	}
2982 	static fre_t re("<MODEM:([[:alnum:]-]+)((:[[:digit:].+-]*)*)>", REG_EXTENDED);
2983 
2984 	std::string testmode = s.substr(i, endbracket - i + 1);
2985 	for (int i = 2; i < 7; i++) testmode[i] = toupper(testmode[i]);
2986 
2987 	std::string name = testmode;
2988 	name.erase(0,7);
2989 	name.erase(name.length() - 1);
2990 	name = ucasestr(name);
2991 
2992 // test for exact match on mode name (ignore case)
2993 
2994 	for (trx_mode m = 0; m < NUM_MODES; m++) {
2995 		if (name == ucasestr(mode_info[m].sname)) {
2996 			init_modem(mode_info[m].mode);
2997 			s.erase(i, endbracket - i + 1);
2998 			int count = 500;
2999 			while ((active_modem->get_mode() != mode_info[m].mode) && --count)
3000 				MilliSleep(10);
3001 			return;
3002 		}
3003 	}
3004 
3005 	if (!re.match(testmode.c_str())) {
3006 		s.erase(i, endbracket - i + 1);
3007 		return;
3008 	}
3009 
3010 	const std::vector<regmatch_t>& o = re.suboff();
3011 	name = ucasestr(testmode.substr(o[1].rm_so, o[1].rm_eo - o[1].rm_so));
3012 	trx_mode m;
3013 	for (m = 0; m < NUM_MODES; m++)
3014 		if (name == ucasestr(mode_info[m].sname))
3015 			break;
3016 	// do we have arguments and a valid modem?
3017 	if (o.size() == 2 || m == NUM_MODES) {
3018 		if (m < NUM_MODES && active_modem->get_mode() != mode_info[m].mode)
3019 			init_modem(mode_info[m].mode);
3020 		s.erase(i, o[0].rm_eo - i);
3021 		int count = 500;
3022 		while ((active_modem->get_mode() != mode_info[m].mode) && --count)
3023 			MilliSleep(10);
3024 		return;
3025 	}
3026 
3027 	// parse arguments
3028 	vector<double> args;
3029 	args.reserve(8);
3030 	char* end;
3031 	double d;
3032 	for (const char* p = testmode.c_str() + o[2].rm_so + 1; *p; p++) {
3033 		errno = 0;
3034 		d = strtod(p, &end);
3035 		if (!errno && p != end) {
3036 			args.push_back(d);
3037 			p = end;
3038 		}
3039 		else // push an invalid value
3040 			args.push_back(DBL_MIN);
3041 	}
3042 
3043 	try {
3044 		switch (m) {
3045 			case MODE_RTTY: // carrier shift, baud rate, bits per char
3046 				if (args.at(0) != DBL_MIN)
3047 					set_rtty_shift((int)args[0]);
3048 				if (args.at(1) != DBL_MIN)
3049 					set_rtty_baud((float)args[1]);
3050 				if (args.at(2) != DBL_MIN)
3051 					set_rtty_bits((int)args[2]);
3052 				break;
3053 			case MODE_CONTESTIA: // bandwidth, tones
3054 				if (args.at(0) != DBL_MIN && args.at(1) != DBL_MIN) {
3055 					int bw = (int)args[0];
3056 					int tones = (int)args[1];
3057 					set_contestia_bw(bw);
3058 					set_contestia_tones(tones);
3059 					switch (tones) {
3060 						case 4 :
3061 							if (bw == 125) m = MODE_CONTESTIA_4_125;
3062 							else if (bw == 250) m = MODE_CONTESTIA_4_250;
3063 							else if (bw == 500) m = MODE_CONTESTIA_4_500;
3064 							else if (bw == 1000) m = MODE_CONTESTIA_4_1000;
3065 							else if (bw == 2000) m = MODE_CONTESTIA_4_2000;
3066 							else {
3067 								set_contestia_bw(bw);
3068 								set_contestia_tones(tones);
3069 							}
3070 							break;
3071 						case 8 :
3072 							if (bw == 125) m = MODE_CONTESTIA_8_125;
3073 							else if (bw == 250) m = MODE_CONTESTIA_8_250;
3074 							else if (bw == 500) m = MODE_CONTESTIA_8_500;
3075 							else if (bw == 1000) m = MODE_CONTESTIA_8_1000;
3076 							else if (bw == 2000) m = MODE_CONTESTIA_8_2000;
3077 							else {
3078 								set_contestia_bw(bw);
3079 								set_contestia_tones(tones);
3080 							}
3081 							break;
3082 						case 16 :
3083 							if (bw == 250) m = MODE_CONTESTIA_16_250;
3084 							else if (bw == 500) m = MODE_CONTESTIA_16_500;
3085 							else if (bw == 1000) m = MODE_CONTESTIA_16_1000;
3086 							else if (bw == 2000) m = MODE_CONTESTIA_16_2000;
3087 							else {
3088 								set_contestia_bw(bw);
3089 								set_contestia_tones(tones);
3090 							}
3091 							break;
3092 						case 32 :
3093 							if (bw == 1000) m = MODE_CONTESTIA_32_1000;
3094 							else if (bw == 2000) m = MODE_CONTESTIA_32_2000;
3095 							else {
3096 								set_contestia_bw(bw);
3097 								set_contestia_tones(tones);
3098 							}
3099 							break;
3100 						case 64 :
3101 							if (bw == 500) m = MODE_CONTESTIA_64_500;
3102 							else if (bw == 1000) m = MODE_CONTESTIA_64_1000;
3103 							else if (bw == 2000) m = MODE_CONTESTIA_64_2000;
3104 							else {
3105 								set_contestia_bw(bw);
3106 								set_contestia_tones(tones);
3107 							}
3108 							break;
3109 						default :
3110 							set_contestia_bw(bw);
3111 							set_contestia_tones(tones);
3112 					}
3113 				}
3114 				break;
3115 			case MODE_OLIVIA: // bandwidth, tones
3116 				if (args.at(0) != DBL_MIN && args.at(1) != DBL_MIN) {
3117 					int bw = (int)args[0];
3118 					int tones = (int)args[1];
3119 					set_olivia_bw(bw);
3120 					set_olivia_tones(tones);
3121 					switch (tones) {
3122 						case 4 :
3123 							if (bw == 125) m = MODE_OLIVIA_4_125;
3124 							else if (bw == 250) m = MODE_OLIVIA_4_250;
3125 							else if (bw == 500) m = MODE_OLIVIA_4_500;
3126 							else if (bw == 1000) m = MODE_OLIVIA_4_1000;
3127 							else if (bw == 2000) m = MODE_OLIVIA_4_2000;
3128 							else  {
3129 								set_olivia_bw(bw);
3130 								set_olivia_tones(tones);
3131 							}
3132 							break;
3133 						case 8 :
3134 							if (bw == 125) m = MODE_OLIVIA_8_125;
3135 							else if (bw == 250) m = MODE_OLIVIA_8_250;
3136 							else if (bw == 500) m = MODE_OLIVIA_8_500;
3137 							else if (bw == 1000) m = MODE_OLIVIA_8_1000;
3138 							else if (bw == 2000) m = MODE_OLIVIA_8_2000;
3139 							else  {
3140 								set_olivia_bw(bw);
3141 								set_olivia_tones(tones);
3142 							}
3143 							break;
3144 						case 16 :
3145 							if (bw == 500) m = MODE_OLIVIA_16_500;
3146 							else if (bw == 1000) m = MODE_OLIVIA_16_1000;
3147 							else if (bw == 2000) m = MODE_OLIVIA_16_2000;
3148 							else  {
3149 								set_olivia_bw(bw);
3150 								set_olivia_tones(tones);
3151 							}
3152 							break;
3153 						case 32 :
3154 							if (bw == 1000) m = MODE_OLIVIA_32_1000;
3155 							else if (bw == 2000) m = MODE_OLIVIA_32_2000;
3156 							else  {
3157 								set_olivia_bw(bw);
3158 								set_olivia_tones(tones);
3159 							}
3160 							break;
3161 						case 64 :
3162 							if (bw == 500) m = MODE_OLIVIA_64_500;
3163 							else if (bw == 1000) m = MODE_OLIVIA_64_1000;
3164 							else if (bw == 2000) m = MODE_OLIVIA_64_2000;
3165 							else  {
3166 								set_olivia_bw(bw);
3167 								set_olivia_tones(tones);
3168 							}
3169 							break;
3170 						default :
3171 							set_olivia_bw(bw);
3172 							set_olivia_tones(tones);
3173 					}
3174 				}
3175 				break;
3176 			default:
3177 				break;
3178 		}
3179 	}
3180 	catch (const exception& e) { }
3181 
3182 	if (active_modem->get_mode() != mode_info[m].mode) {
3183 		init_modem(mode_info[m].mode);
3184 		int count = 500;
3185 		while ((active_modem->get_mode() != mode_info[m].mode) && --count)
3186 			MilliSleep(10);
3187 	}
3188 
3189 	substitute(s, i, endbracket, "");
3190 }
3191 
3192 static void pAFC(std::string &s, size_t &i, size_t endbracket)
3193 {
3194 	if (within_exec) {
3195 		substitute(s, i, endbracket, "");
3196 		return;
3197 	}
3198 	std::string sVal = s.substr(i+5, endbracket - i - 5);
3199 	if (sVal.length() > 0) {
3200 		// sVal = on|off|t   [ON, OFF or Toggle]
3201 		if (sVal.compare(0,2,"on") == 0)
3202 			btnAFC->value(1);
3203 		else if (sVal.compare(0,3,"off") == 0)
3204 			btnAFC->value(0);
3205 		else if (sVal.compare(0,1,"t") == 0)
3206 			btnAFC->value(!btnAFC->value());
3207 
3208 		btnAFC->do_callback();
3209 	}
3210 	substitute(s, i, endbracket, "");
3211 }
3212 
3213 static void pREV(std::string &s, size_t &i, size_t endbracket)
3214 {
3215 	if (within_exec) {
3216 		substitute(s, i, endbracket, "");
3217 		return;
3218 	}
3219 	std::string sVal = s.substr(i+5, endbracket - i - 5);
3220 	if (sVal.length() > 0) {
3221 		// sVal = on|off|t   [ON, OFF or Toggle]
3222 		if (sVal.compare(0,2,"on") == 0)
3223 			wf->btnRev->value(1);
3224 		else if (sVal.compare(0,3,"off") == 0)
3225 			wf->btnRev->value(0);
3226 		else if (sVal.compare(0,1,"t") == 0)
3227 			wf->btnRev->value(!wf->btnRev->value());
3228 
3229 		wf->btnRev->do_callback();
3230 	}
3231 	substitute(s, i, endbracket, "");
3232 }
3233 
3234 // <HS:on|off|t>
3235 static void pHS(std::string &s, size_t &i, size_t endbracket)
3236 {
3237 	if (within_exec) {
3238 		substitute(s, i, endbracket, "");
3239 		return;
3240 	}
3241 	std::string sVal = s.substr(i+4, endbracket - i - 4);
3242 	if (sVal.length() > 0) {
3243 		// sVal = on|off|t   [ON, OFF or Toggle]
3244 		if (sVal.compare(0,2,"on") == 0)
3245 			bHighSpeed = 1;
3246 		else if (sVal.compare(0,3,"off") == 0)
3247 			bHighSpeed = 0;
3248 		else if (sVal.compare(0,1,"t") == 0)
3249 			bHighSpeed = !bHighSpeed;
3250 	}
3251 	substitute(s, i, endbracket, "");
3252 }
3253 
3254 
3255 static void pLOCK(std::string &s, size_t &i, size_t endbracket)
3256 {
3257 	if (within_exec) {
3258 		substitute(s, i, endbracket, "");
3259 		return;
3260 	}
3261 	std::string sVal = s.substr(i+6, endbracket - i - 6);
3262 	if (sVal.length() > 0) {
3263 		// sVal = on|off|t   [ON, OFF or Toggle]
3264 		if (sVal.compare(0,2,"on") == 0)
3265 			wf->xmtlock->value(1);
3266 		else if (sVal.compare(0,3,"off") == 0)
3267 			wf->xmtlock->value(0);
3268 		else if (sVal.compare(0,1,"t") == 0)
3269 			wf->xmtlock->value(!wf->xmtlock->value());
3270 
3271 		wf->xmtlock->damage();
3272 		wf->xmtlock->do_callback();
3273 	}
3274 	substitute(s, i, endbracket, "");
3275 }
3276 
3277 static void doLOCK( std::string s){
3278 	std::string sVal = s.substr(7, s.length() - 8);
3279 	if (sVal.length() > 0) {
3280 		// sVal = on|off|t[oggle]   [ON, OFF or Toggle]
3281 		if (sVal.compare(0,2,"on") == 0)
3282 			wf->xmtlock->value(1);
3283 		else if (sVal.compare(0,3,"off") == 0)
3284 			wf->xmtlock->value(0);
3285 		else if (sVal.compare(0,1,"t") == 0)
3286 			wf->xmtlock->value(!wf->xmtlock->value());
3287 		wf->xmtlock->damage();
3288 		wf->xmtlock->do_callback();
3289 	}
3290 }
3291 
3292 static void pRxQueLOCK(std::string &s, size_t &i, size_t endbracket)
3293 {
3294 	if (within_exec) {
3295 		substitute(s, i, endbracket, "");
3296 		return;
3297 	}
3298 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doLOCK };
3299 	push_rxcmd(cmd);
3300 	substitute(s, i, endbracket, "");
3301 }
3302 
3303 
3304 static void pTX_RSID(std::string &s, size_t &i, size_t endbracket)
3305 {
3306 	if (within_exec) {
3307 		substitute(s, i, endbracket, "");
3308 		return;
3309 	}
3310 	std::string sVal = s.substr(i+8, endbracket - i - 8);
3311 	if (sVal.length() > 0) {
3312 		// sVal = on|off|t   [ON, OFF or Toggle]
3313 		if (sVal.compare(0,2,"on") == 0)
3314 			btnTxRSID->value(1);
3315 		else if (sVal.compare(0,3,"off") == 0)
3316 			btnTxRSID->value(0);
3317 		else if (sVal.compare(0,1,"t") == 0)
3318 			btnTxRSID->value(!btnTxRSID->value());
3319 		btnTxRSID->do_callback();
3320 	}
3321 	substitute(s, i, endbracket, "");
3322 }
3323 
3324 static void doTXRSID(std::string s)
3325 {
3326 	if (s.find("on") != std::string::npos) {
3327 		btnTxRSID->value(1);
3328 		btnTxRSID->do_callback();
3329 	}
3330 	else if (s.find("off") != std::string::npos) {
3331 		btnTxRSID->value(0);
3332 		btnTxRSID->do_callback();
3333 	}
3334 	else if (s.find("t") != std::string::npos) {
3335 		btnTxRSID->value(!btnTxRSID->value());
3336 		btnTxRSID->do_callback();
3337 	}
3338 	que_ok = true;
3339 }
3340 
3341 static void pRxQueTXRSID(std::string &s, size_t &i, size_t endbracket)
3342 {
3343 	if (within_exec) {
3344 		substitute(s, i, endbracket, "");
3345 		return;
3346 	}
3347 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doTXRSID };
3348 	push_rxcmd(cmd);
3349 	substitute(s, i, endbracket, "");
3350 }
3351 
3352 static void pRX_RSID(std::string &s, size_t &i, size_t endbracket)
3353 {
3354 	if (within_exec) {
3355 		substitute(s, i, endbracket, "");
3356 		return;
3357 	}
3358 	std::string sVal = s.substr(i+8, endbracket - i - 8);
3359 	if (sVal.length() > 0) {
3360 		// sVal = on|off|t   [ON, OFF or Toggle]
3361 		if (sVal.compare(0,2,"on") == 0)
3362 			btnRSID->value(1);
3363 		else if (sVal.compare(0,3,"off") == 0)
3364 			btnRSID->value(0);
3365 		else if (sVal.compare(0,1,"t") == 0)
3366 			btnRSID->value(!btnRSID->value());
3367 
3368 		btnRSID->do_callback();
3369 	}
3370 	substitute(s, i, endbracket, "");
3371 }
3372 
3373 static void pCSV(std::string &s, size_t &i, size_t endbracket)
3374 {
3375 	if (within_exec) {
3376 		substitute(s, i, endbracket, "");
3377 		return;
3378 	}
3379 	std::string sVal = s.substr(i+5, endbracket - i - 5);
3380 	if (sVal.length() > 0) {
3381 		// sVal = on|off   [ON, OFF]
3382 		if (sVal.compare(0,2,"on") == 0)
3383 			set_CSV(1);
3384 		else if (sVal.compare(0,3,"off") == 0)
3385 			set_CSV(0);
3386 		else if (sVal.compare(0,1,"t") == 0)
3387 			set_CSV(2);
3388 	}
3389 	substitute(s, i, endbracket, "");
3390 }
3391 
3392 #ifdef __WIN32__
3393 static void pTALK(std::string &s, size_t &i, size_t endbracket)
3394 {
3395 	if (within_exec) {
3396 		substitute(s, i, endbracket, "");
3397 		return;
3398 	}
3399 	std::string sVal = s.substr(i+6, endbracket - i - 6);
3400 	if (sVal.length() > 0) {
3401 		// sVal = on|off   [ON, OFF]
3402 		if (sVal.compare(0,2,"on") == 0)
3403 			open_talker();
3404 		else if (sVal.compare(0,3,"off") == 0)
3405 			close_talker();
3406 		else if (sVal.compare(0,1,"t") == 0)
3407 			toggle_talker();
3408 	}
3409 	substitute(s, i, endbracket, "");
3410 }
3411 #endif
3412 
3413 static void pSRCHUP(std::string &s, size_t &i, size_t endbracket)
3414 {
3415 	if (within_exec) {
3416 		substitute(s, i, endbracket, "");
3417 		return;
3418 	}
3419 	substitute(s, i, endbracket, "");
3420 	active_modem->searchUp();
3421 	if (progdefaults.WaterfallClickInsert)
3422 		wf->insert_text(true);
3423 }
3424 
3425 static void pSRCHDN(std::string &s, size_t &i, size_t endbracket)
3426 {
3427 	if (within_exec) {
3428 		substitute(s, i, endbracket, "");
3429 		return;
3430 	}
3431 	substitute(s, i, endbracket, "");
3432 	active_modem->searchDown();
3433 	if (progdefaults.WaterfallClickInsert)
3434 		wf->insert_text(true);
3435 }
3436 
3437 static void pGOHOME(std::string &s, size_t &i, size_t endbracket)
3438 {
3439 	if (within_exec) {
3440 		substitute(s, i, endbracket, "");
3441 		return;
3442 	}
3443 	substitute(s, i, endbracket, "");
3444 	if (active_modem == cw_modem)
3445 		active_modem->set_freq(progdefaults.CWsweetspot);
3446 	else if (active_modem == rtty_modem)
3447 		active_modem->set_freq(progdefaults.RTTYsweetspot);
3448 	else
3449 		active_modem->set_freq(progdefaults.PSKsweetspot);
3450 }
3451 
3452 static void doGOHOME(std::string s)
3453 {
3454 	if (active_modem == cw_modem)
3455 		active_modem->set_freq(progdefaults.CWsweetspot);
3456 	else if (active_modem == rtty_modem)
3457 		active_modem->set_freq(progdefaults.RTTYsweetspot);
3458 	else
3459 		active_modem->set_freq(progdefaults.PSKsweetspot);
3460 	que_ok = true;
3461 }
3462 
3463 static void pTxQueGOHOME(std::string &s, size_t &i, size_t endbracket)
3464 {
3465 	if (within_exec) {
3466 		substitute(s, i, endbracket, "");
3467 		return;
3468 	}
3469 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doGOHOME };
3470 	push_txcmd(cmd);
3471 	substitute(s, i, endbracket, "^!");
3472 }
3473 
3474 static void pRxQueGOHOME(std::string &s, size_t &i, size_t endbracket)
3475 {
3476 	if (within_exec) {
3477 		substitute(s, i, endbracket, "");
3478 		return;
3479 	}
3480 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doGOHOME };
3481 	push_rxcmd(cmd);
3482 	substitute(s, i, endbracket, "");
3483 }
3484 
3485 static void pGOFREQ(std::string &s, size_t &i, size_t endbracket)
3486 {
3487 	if (within_exec) {
3488 		substitute(s, i, endbracket, "");
3489 		return;
3490 	}
3491 	int number;
3492 	std::string sGoFreq = s.substr(i+8, endbracket - i - 8);
3493 	if (sGoFreq.length() > 0) {
3494 		sscanf(sGoFreq.c_str(), "%d", &number);
3495 		if (number < progdefaults.LowFreqCutoff)
3496 			number = progdefaults.LowFreqCutoff;
3497 		if (number > progdefaults.HighFreqCutoff)
3498 			number = progdefaults.HighFreqCutoff;
3499 		active_modem->set_freq(number);
3500 	}
3501 	substitute(s, i, endbracket, "");
3502 }
3503 
3504 static void doGOFREQ(std::string s)
3505 {
3506 	int number;
3507 	std::string sGoFreq = s.substr(9, s.length() - 10);
3508 	if (sGoFreq.length() > 0) {
3509 		sscanf(sGoFreq.c_str(), "%d", &number);
3510 		if (number < progdefaults.LowFreqCutoff)
3511 			number = progdefaults.LowFreqCutoff;
3512 		if (number > progdefaults.HighFreqCutoff)
3513 			number = progdefaults.HighFreqCutoff;
3514 		active_modem->set_freq(number);
3515 	}
3516 	que_ok = true;
3517 }
3518 
3519 static void pTxQueGOFREQ(std::string &s, size_t &i, size_t endbracket)
3520 {
3521 	if (within_exec) {
3522 		s.replace(i, endbracket - i + 1, "");
3523 		return;
3524 	}
3525 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doGOFREQ };
3526 	push_txcmd(cmd);
3527 	substitute(s, i, endbracket, "^!");
3528 }
3529 
3530 static void pRxQueGOFREQ(std::string &s, size_t &i, size_t endbracket)
3531 {
3532 	if (within_exec) {
3533 		substitute(s, i, endbracket, "");
3534 		return;
3535 	}
3536 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doGOFREQ };
3537 	push_rxcmd(cmd);
3538 	substitute(s, i, endbracket, "");
3539 }
3540 
3541 static void pQRG(std::string &s, size_t &i, size_t endbracket)
3542 {
3543 	if (within_exec) {
3544 		substitute(s, i, endbracket, "");
3545 		return;
3546 	}
3547 	std::string prefix = "\n";
3548 	prefix.append(s.substr(i+5, endbracket - i - 5));
3549 	if (prefix.length()) note_qrg ( false, prefix.c_str(), "\n" );
3550 	substitute(s, i, endbracket, "");
3551 }
3552 
3553 static void pQSYTO(std::string &s, size_t &i, size_t endbracket)
3554 {
3555 	if (within_exec) {
3556 		substitute(s, i, endbracket, "");
3557 		return;
3558 	}
3559 	substitute(s, i, endbracket, "");
3560 	do_qsy(true);
3561 }
3562 
3563 static void pQSYFM(std::string &s, size_t &i, size_t endbracket)
3564 {
3565 	if (within_exec) {
3566 		substitute(s, i, endbracket, "");
3567 		return;
3568 	}
3569 	substitute(s, i, endbracket, "");
3570 	do_qsy(false);
3571 }
3572 
3573 struct rfafmd { int rf; int af; std::string mdname;
3574 	rfafmd(int a, int b, std::string nm) { rf = a; af = b; mdname = nm;}
3575 	rfafmd(int a, int b) {rf = a; af = b; mdname = active_modem->get_mode_name();}
3576 	rfafmd(){rf = af = 0; mdname = active_modem->get_mode_name();}
3577 };
3578 static queue<rfafmd> fpairs;
3579 
3580 static void pQSY(std::string &s, size_t &i, size_t endbracket)
3581 {
3582 	if (within_exec) {
3583 		substitute(s, i, endbracket, "");
3584 		return;
3585 	}
3586 
3587 	std::string mdname = active_modem->get_mode_name();
3588 	int rf = 0;
3589 	int af = 0;
3590 	float rfd = 0;
3591 	std::string sGoFreq = s.substr(i+5, endbracket - i - 5);
3592 	// no frequency(s) specified
3593 	if (sGoFreq.length() == 0) {
3594 		substitute(s, i, endbracket, "");
3595 		return;
3596 	}
3597 
3598 	if (fpairs.empty()) {
3599 		std::string triad;
3600 		size_t pos;
3601 		while (!sGoFreq.empty()) {
3602 			pos = sGoFreq.find(";");
3603 			if (pos == std::string::npos) triad = sGoFreq;
3604 			else  triad = sGoFreq.substr(0, pos);
3605 			sGoFreq.erase(0, triad.length()+1);
3606 			sscanf(triad.c_str(), "%f", &rfd);
3607 			if (rfd > 0) rf = (int)(1000*rfd);
3608 			if ((pos = triad.find(":")) != std::string::npos) {
3609 				triad.erase(0,pos+1);
3610 				if (triad.length())
3611 					sscanf(triad.c_str(), "%d", &af);
3612 				if (af < 0) af = 0;
3613 				if (af < progdefaults.LowFreqCutoff) af = progdefaults.LowFreqCutoff;
3614 				if (af > progdefaults.HighFreqCutoff) af = progdefaults.HighFreqCutoff;
3615 			} else af = active_modem->get_freq();
3616 
3617 			if ((pos = triad.find(":")) != std::string::npos) {
3618 				triad.erase(0, pos+1);
3619 				strtrim(triad);
3620 				fpairs.push(rfafmd(rf, af, triad));
3621 			} else
3622 				fpairs.push(rfafmd(rf,af, mdname));
3623 		}
3624 	}
3625 
3626 	struct rfafmd fpair;
3627 
3628 	fpair = fpairs.front();
3629 	rf = fpair.rf;
3630 	af = fpair.af;
3631 	if (fpair.mdname != mdname) {
3632 		for (int m = 0; m < NUM_MODES; m++) {
3633 			if (fpair.mdname == mode_info[m].sname) {
3634 				init_modem_sync(mode_info[m].mode);
3635 				break;
3636 			}
3637 		}
3638 	}
3639 	fpairs.pop();
3640 
3641 	if (rf && rf != wf->rfcarrier())
3642 		qsy(rf, af);
3643 	else
3644 		active_modem->set_freq(af);
3645 
3646 	substitute(s, i, endbracket, "");
3647 }
3648 
3649 static void doQSY(std::string s)
3650 {
3651 	int rf = 0;
3652 	int audio = 0;
3653 	float rfd = 0;
3654 	std::string sGoFreq;
3655 	sGoFreq = s.substr(6, s.length() - 7);
3656 	// no frequency(s) specified
3657 	if (sGoFreq.length() == 0) {
3658 		que_ok = true;
3659 		return;
3660 	}
3661 	// rf first value
3662 	sscanf(sGoFreq.c_str(), "%f", &rfd);
3663 	if (rfd > 0)
3664 		rf = (int)(1000*rfd);
3665 	size_t pos;
3666 	if ((pos = sGoFreq.find(":")) != std::string::npos) {
3667 		// af second value
3668 		sGoFreq.erase(0, pos+1);
3669 		if (sGoFreq.length())
3670 			sscanf(sGoFreq.c_str(), "%d", &audio);
3671 		if (audio < 0) audio = 0;
3672 		if (audio < progdefaults.LowFreqCutoff)
3673 			audio = progdefaults.LowFreqCutoff;
3674 		if (audio > progdefaults.HighFreqCutoff)
3675 			audio = progdefaults.HighFreqCutoff;
3676 	}
3677 	if (rf && rf != wf->rfcarrier())
3678 		qsy(rf, audio);
3679 	else
3680 		active_modem->set_freq(audio);
3681 	que_ok = true;
3682 }
3683 
3684 static void pTxQueQSY(std::string &s, size_t &i, size_t endbracket)
3685 {
3686 	if (within_exec) {
3687 		substitute(s, i, endbracket, "");
3688 		return;
3689 	}
3690 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doQSY };
3691 	push_txcmd(cmd);
3692 	substitute(s, i, endbracket, "^!");
3693 }
3694 
3695 float  wait_after_mode_change = 0.0;
3696 static string sFILWID;
3697 static void delayedFILWID(void *)
3698 {
3699 	qso_opBW->value(sFILWID.c_str());
3700 	cb_qso_opBW();
3701 	wait_after_mode_change = 0.0;
3702 }
3703 
3704 static void pFILWID(std::string& s, size_t& i, size_t endbracket)
3705 {
3706 	if (within_exec) {
3707 		substitute(s, i, endbracket, "");
3708 		return;
3709 	}
3710 	std::string sWidth = s.substr(i+8, endbracket - i - 8);
3711 	sFILWID = sWidth;
3712 	Fl::add_timeout(wait_after_mode_change, delayedFILWID);
3713 	substitute(s, i, endbracket, "");
3714 }
3715 
3716 static void doFILWID(std::string s)
3717 {
3718 	std::string sWID = s.substr(9, s.length() - 10);
3719 	qso_opBW->value(sWID.c_str());
3720 	cb_qso_opBW();
3721 	que_ok = true;
3722 }
3723 
3724 static void pTxQueFILWID(std::string &s, size_t &i, size_t endbracket)
3725 {
3726 	if (within_exec) {
3727 		substitute(s, i, endbracket, "");
3728 		return;
3729 	}
3730 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doFILWID };
3731 	push_txcmd(cmd);
3732 	substitute(s, i, endbracket, "^!");
3733 }
3734 
3735 static void pRxQueFILWID(std::string &s, size_t &i, size_t endbracket)
3736 {
3737 	if (within_exec) {
3738 		substitute(s, i, endbracket, "");
3739 		return;
3740 	}
3741 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doFILWID };
3742 	push_rxcmd(cmd);
3743 	substitute(s, i, endbracket, "");
3744 }
3745 
3746 static void pRIGMODE(std::string& s, size_t& i, size_t endbracket)
3747 {
3748 	if (within_exec) {
3749 		substitute(s, i, endbracket, "");
3750 		return;
3751 	}
3752 	std::string sMode = s.substr(i+9, endbracket - i - 9);
3753 	qso_opMODE->value(sMode.c_str());
3754 	cb_qso_opMODE();
3755 	substitute(s, i, endbracket, "");
3756 	if ((s.find("FILWID") != string::npos) ||
3757 		(s.find("RIGLO") != string::npos) ||
3758 		(s.find("RIGHI") != string::npos) )
3759 		wait_after_mode_change = progdefaults.mbw;
3760 	else
3761 		wait_after_mode_change = 0;
3762 }
3763 
3764 static void doRIGMODE(std::string s)
3765 {
3766 	std::string sMode = s.substr(10, s.length() - 11);
3767 	qso_opMODE->value(sMode.c_str());
3768 	cb_qso_opMODE();
3769 	que_ok = true;
3770 }
3771 
3772 static void pTxQueRIGMODE(std::string &s, size_t &i, size_t endbracket)
3773 {
3774 	if (within_exec) {
3775 		substitute(s, i, endbracket, "");
3776 		return;
3777 	}
3778 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRIGMODE };
3779 	push_txcmd(cmd);
3780 	substitute(s, i, endbracket, "^!");
3781 }
3782 
3783 static void pRxQueRIGMODE(std::string &s, size_t &i, size_t endbracket)
3784 {
3785 	if (within_exec) {
3786 		substitute(s, i, endbracket, "");
3787 		return;
3788 	}
3789 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRIGMODE };
3790 	push_rxcmd(cmd);
3791 	substitute(s, i, endbracket, "");
3792 }
3793 
3794 static string sRIGLO;
3795 
3796 static void delayedRIGLO(void *)
3797 {
3798 	qso_opBW2->value(sRIGLO.c_str());
3799 	cb_qso_opBW2();
3800 	wait_after_mode_change = 0.0;
3801 }
3802 
3803 static void pRIGLO(std::string& s, size_t& i, size_t endbracket)
3804 {
3805 	if (within_exec) {
3806 		substitute(s, i, endbracket, "");
3807 		return;
3808 	}
3809 	std::string sLO = s.substr(i+7, endbracket - i - 7);
3810 	sRIGLO = sLO;
3811 	if (wait_after_mode_change)
3812 		Fl::add_timeout(wait_after_mode_change, delayedRIGLO);
3813 	else {
3814 		qso_opBW2->value(sLO.c_str());
3815 		cb_qso_opBW2();
3816 	}
3817 	substitute(s, i, endbracket, "");
3818 }
3819 
3820 static void doRIGLO(std::string s)
3821 {
3822 	std::string sLO = s.substr(8, s.length() - 9);
3823 	qso_opBW2->value(sLO.c_str());
3824 	cb_qso_opBW2();
3825 	que_ok = true;
3826 }
3827 
3828 static void pTxQueRIGLO(std::string &s, size_t &i, size_t endbracket)
3829 {
3830 	if (within_exec) {
3831 		substitute(s, i, endbracket, "");
3832 		return;
3833 	}
3834 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRIGLO };
3835 	push_txcmd(cmd);
3836 	substitute(s, i, endbracket, "^!");
3837 }
3838 
3839 static void pRxQueRIGLO(std::string &s, size_t &i, size_t endbracket)
3840 {
3841 	if (within_exec) {
3842 		substitute(s, i, endbracket, "");
3843 		return;
3844 	}
3845 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRIGLO };
3846 	push_rxcmd(cmd);
3847 	substitute(s, i, endbracket, "");
3848 }
3849 
3850 static string sRIGHI;
3851 
3852 static void delayedRIGHI(void *)
3853 {
3854 	qso_opBW1->value(sRIGHI.c_str());
3855 	cb_qso_opBW1();
3856 	wait_after_mode_change = 0.0;
3857 }
3858 
3859 static void pRIGHI(std::string& s, size_t& i, size_t endbracket)
3860 {
3861 	if (within_exec) {
3862 		substitute(s, i, endbracket, "");
3863 		return;
3864 	}
3865 	std::string sHI = s.substr(i+7, endbracket - i - 7);
3866 	sRIGHI = sHI;
3867 	if (wait_after_mode_change)
3868 		Fl::add_timeout(wait_after_mode_change, delayedRIGHI);
3869 	else {
3870 		qso_opBW1->value(sHI.c_str());
3871 		cb_qso_opBW1();
3872 	}
3873 	substitute(s, i, endbracket, "");
3874 }
3875 
3876 static void doRIGHI(std::string s)
3877 {
3878 	std::string sHI = s.substr(8, s.length() - 9);
3879 	qso_opBW1->value(sHI.c_str());
3880 	cb_qso_opBW1();
3881 	que_ok = true;
3882 }
3883 
3884 static void pTxQueRIGHI(std::string &s, size_t &i, size_t endbracket)
3885 {
3886 	if (within_exec) {
3887 		substitute(s, i, endbracket, "");
3888 		return;
3889 	}
3890 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRIGHI };
3891 	push_txcmd(cmd);
3892 	substitute(s, i, endbracket, "^!");
3893 }
3894 
3895 static void pRxQueRIGHI(std::string &s, size_t &i, size_t endbracket)
3896 {
3897 	if (within_exec) {
3898 		substitute(s, i, endbracket, "");
3899 		return;
3900 	}
3901 	struct CMDS cmd = { s.substr(i, endbracket - i + 1), doRIGHI };
3902 	push_rxcmd(cmd);
3903 	substitute(s, i, endbracket, "");
3904 }
3905 
3906 static void pWX(std::string &s, size_t &i, size_t endbracket)
3907 {
3908 	string wx;
3909 	getwx(wx);
3910 	s.replace(i, 4, wx);
3911 }
3912 
3913 // <WX:metar>
3914 static void pWX2(std::string &s, size_t &i, size_t endbracket)
3915 {
3916 	string wx;
3917 	getwx(wx, s.substr(i+4, endbracket - i - 4).c_str());
3918 	substitute(s, i, endbracket, wx);
3919 }
3920 
3921 
3922 void set_macro_env(void)
3923 {
3924 	enum {
3925 #ifndef __WOE32__
3926 		pSKEDH, FLDIGI_RX_IPC_KEY, FLDIGI_TX_IPC_KEY,
3927 #endif
3928 		FLDIGI_XMLRPC_ADDRESS,
3929 		FLDIGI_XMLRPC_PORT,
3930 		FLDIGI_ARQ_ADDRESS,
3931 		FLDIGI_ARQ_PORT,
3932 
3933 		FLDIGI_VERSION_ENVVAR,
3934 		FLDIGI_PID,
3935 		FLDIGI_CONFIG_DIR,
3936 
3937 		FLDIGI_MY_CALL,
3938 		FLDIGI_MY_NAME,
3939 		FLDIGI_MY_LOCATOR,
3940 
3941 		FLDIGI_MODEM,
3942 		FLDIGI_MODEM_LONG_NAME,
3943 		FLDIGI_MODEM_ADIF_NAME,
3944 		FLDIGI_DIAL_FREQUENCY,
3945 		FLDIGI_AUDIO_FREQUENCY,
3946 		FLDIGI_FREQUENCY,
3947 
3948 		FLDIGI_MACRO_FILE,
3949 
3950 		FLDIGI_LOG_FILE,
3951 		FLDIGI_LOG_FREQUENCY,
3952 		FLDIGI_LOG_DATE,
3953 		FLDIGI_LOG_DATE_OFF,
3954 		FLDIGI_LOG_TIME_ON,
3955 		FLDIGI_LOG_TIME_OFF,
3956 		FLDIGI_LOG_CALL,
3957 		FLDIGI_LOG_NAME,
3958 		FLDIGI_LOG_RST_IN,
3959 		FLDIGI_LOG_RST_OUT,
3960 		FLDIGI_LOG_QTH,
3961 		FLDIGI_LOG_LOCATOR,
3962 		FLDIGI_LOG_NOTES,
3963 		FLDIGI_LOG_STATE,
3964 		FLDIGI_LOG_COUNTRY,
3965 		FLDIGI_LOG_COUNTY,
3966 		FLDIGI_LOG_SERNO_IN,
3967 		FLDIGI_LOG_SERNO_OUT,
3968 		FLDIGI_XCHG_IN,
3969 		FLDIGI_XCGH_OUT,
3970 		FLDIGI_CLASS_IN,
3971 		FLDIGI_ARRL_SECTION_IN,
3972 		FLDIGI_VE_PROV,
3973 		FLDIGI_AZ,
3974 
3975 		FLDIGI_LOGBOOK_CALL,
3976 		FLDIGI_LOGBOOK_NAME,
3977 		FLDIGI_LOGBOOK_DATE,
3978 		FLDIGI_LOGBOOK_TIME_ON,
3979 		FLDIGI_LOGBOOK_DATE_OFF,
3980 		FLDIGI_LOGBOOK_TIME_OFF,
3981 		FLDIGI_LOGBOOK_RST_IN,
3982 		FLDIGI_LOGBOOK_RST_OUT,
3983 		FLDIGI_LOGBOOK_FREQUENCY,
3984 		FLDIGI_LOGBOOK_MODE,
3985 		FLDIGI_LOGBOOK_STATE,
3986 		FLDIGI_LOGBOOK_VE_PROV,
3987 		FLDIGI_LOGBOOK_COUNTRY,
3988 		FLDIGI_LOGBOOK_SERNO_IN,
3989 		FLDIGI_LOGBOOK_SERNO_OUT,
3990 		FLDIGI_LOGBOOK_XCHG_IN,
3991 		FLDIGI_LOGBOOK_XCHG_OUT,
3992 		FLDIGI_LOGBOOK_CLASS_IN,
3993 		FLDIGI_LOGBOOK_SECTION_IN,
3994 		FLDIGI_LOGBOOK_QTH,
3995 		FLDIGI_LOGBOOK_LOCATOR,
3996 		FLDIGI_LOGBOOK_QSL_R,
3997 		FLDIGI_LOGBOOK_QSL_S,
3998 		FLDIGI_LOGBOOK_NOTES,
3999 		FLDIGI_LOGBOOK_TX_PWR,
4000 		FLDIGI_LOGBOOK_COUNTY,
4001 		FLDIGI_LOGBOOK_IOTA,
4002 		FLDIGI_LOGBOOK_DXCC,
4003 		FLDIGI_LOGBOOK_QSL_VIA,
4004 		FLDIGI_LOGBOOK_CONTINENT,
4005 		FLDIGI_LOGBOOK_CQZ,
4006 		FLDIGI_LOGBOOK_ITUZ,
4007 		FLDIGI_LOGBOOK_SS_SERNO,
4008 		FLDIGI_LOGBOOK_SS_PREC,
4009 		FLDIGI_LOGBOOK_SS_CHK,
4010 		FLDIGI_LOGBOOK_SS_SEC,
4011 
4012 		ENV_SIZE
4013 	};
4014 
4015 	struct {
4016 		const char* var;
4017 		const char* val;
4018 	} env[] = {
4019 #ifndef __WOE32__
4020 		{ "pSKEDH", "" },
4021 		{ "FLDIGI_RX_IPC_KEY", "" },
4022 		{ "FLDIGI_TX_IPC_KEY", "" },
4023 #endif
4024 		{ "FLDIGI_XMLRPC_ADDRESS", progdefaults.xmlrpc_address.c_str() },
4025 		{ "FLDIGI_XMLRPC_PORT", progdefaults.xmlrpc_port.c_str() },
4026 		{ "FLDIGI_ARQ_ADDRESS", progdefaults.arq_address.c_str() },
4027 		{ "FLDIGI_ARQ_PORT", progdefaults.arq_port.c_str() },
4028 
4029 		{ "FLDIGI_VERSION", PACKAGE_VERSION },
4030 		{ "FLDIGI_PID", "" },
4031 		{ "FLDIGI_CONFIG_DIR", HomeDir.c_str() },
4032 
4033 		{ "FLDIGI_MY_CALL", progdefaults.myCall.c_str() },
4034 		{ "FLDIGI_MY_NAME", progdefaults.myName.c_str() },
4035 		{ "FLDIGI_MY_LOCATOR", progdefaults.myLocator.c_str() },
4036 
4037 		{ "FLDIGI_MODEM", mode_info[active_modem->get_mode()].sname },
4038 		{ "FLDIGI_MODEM_LONG_NAME", mode_info[active_modem->get_mode()].name },
4039 		{ "FLDIGI_MODEM_ADIF_NAME", mode_info[active_modem->get_mode()].adif_name },
4040 
4041 		{ "FLDIGI_DIAL_FREQUENCY", "" },
4042 		{ "FLDIGI_AUDIO_FREQUENCY", "" },
4043 		{ "FLDIGI_FREQUENCY", "" },
4044 
4045 		// logging frame
4046 		{ "FLDIGI_MACRO_FILE", progStatus.LastMacroFile.c_str() },
4047 
4048 		{ "FLDIGI_LOG_FILE", progdefaults.logbookfilename.c_str() },
4049 
4050 		{ "FLDIGI_LOG_FREQUENCY", inpFreq->value() },
4051 		{ "FLDIGI_LOG_DATE", inpDate_log->value() },
4052 		{ "FLDIGI_LOG_DATE_OFF", inpDateOff_log->value() },
4053 		{ "FLDIGI_LOG_TIME_ON", inpTimeOn->value() },
4054 		{ "FLDIGI_LOG_TIME_OFF", inpTimeOff->value() },
4055 		{ "FLDIGI_LOG_CALL", inpCall->value() },
4056 		{ "FLDIGI_LOG_NAME", inpName->value() },
4057 		{ "FLDIGI_LOG_RST_IN", inpRstIn->value() },
4058 		{ "FLDIGI_LOG_RST_OUT", inpRstOut->value() },
4059 		{ "FLDIGI_LOG_QTH", inpQth->value() },
4060 		{ "FLDIGI_LOG_LOCATOR", inpLoc->value() },
4061 		{ "FLDIGI_LOG_NOTES", inpNotes->value() },
4062 		{ "FLDIGI_LOG_STATE", inpState->value() },
4063 		{ "FLDIGI_LOG_COUNTRY", cboCountry->value() },
4064 		{ "FLDIGI_LOG_COUNTY", inpCounty->value() },
4065 		{ "FLDIGI_LOG_SERNO_IN", inpSerNo->value() },
4066 		{ "FLDIGI_LOG_SERNO_OUT", outSerNo->value() },
4067 		{ "FLDIGI_XCHG_IN", inpXchgIn->value() },
4068 		{ "FLDIGI_XCHG_OUT", inpSend1->value() },
4069 		{ "FLDIGI_CLASS_IN", inpClass->value() },
4070 		{ "FLDIGI_ARRL_SECTION_IN", inpSection->value() },
4071 		{ "FLDIGI_VE_PROV", inpVEprov->value() },
4072 		{ "FLDIGI_AZ", inpAZ->value() },
4073 
4074 		{ "FLDIGI_LOGBOOK_CALL", inpCall_log->value() },
4075 		{ "FLDIGI_LOGBOOK_NAME", inpName_log->value () },
4076 		{ "FLDIGI_LOGBOOK_DATE", inpDate_log->value() },
4077 		{ "FLDIGI_LOGBOOK_TIME_ON", inpTimeOn_log->value() },
4078 		{ "FLDIGI_LOGBOOK_DATE_OFF", inpDateOff_log->value() },
4079 		{ "FLDIGI_LOGBOOK_TIME_OFF", inpTimeOff_log->value() },
4080 		{ "FLDIGI_LOGBOOK_RST_IN", inpRstR_log->value() },
4081 		{ "FLDIGI_LOGBOOK_RST_OUT", inpRstS_log->value() },
4082 		{ "FLDIGI_LOGBOOK_FREQUENCY", inpFreq_log->value() },
4083 		{ "FLDIGI_LOGBOOK_BAND", inpBand_log->value() },
4084 		{ "FLDIGI_LOGBOOK_MODE", inpMode_log->value() },
4085 		{ "FLDIGI_LOGBOOK_STATE", inpState_log->value() },
4086 		{ "FLDIGI_LOGBOOK_VE_PROV", inpVE_Prov_log->value() },
4087 		{ "FLDIGI_LOGBOOK_COUNTRY", inpCountry_log->value() },
4088 		{ "FLDIGI_LOGBOOK_SERNO_IN", inpSerNoIn_log->value() },
4089 		{ "FLDIGI_LOGBOOK_SERNO_OUT", inpSerNoOut_log->value() },
4090 		{ "FLDIGI_LOGBOOK_XCHG_IN", inpXchgIn_log->value() },
4091 		{ "FLDIGI_LOGBOOK_XCHG_OUT", inpMyXchg_log->value() },
4092 		{ "FLDIGI_LOGBOOK_CLASS_IN", inpClass_log->value() },
4093 		{ "FLDIGI_LOGBOOK_ARRL_SECT_IN", inpSection_log->value() },
4094 		{ "FLDIGI_LOGBOOK_QTH", inpQth_log->value() },
4095 		{ "FLDIGI_LOGBOOK_LOCATOR", inpLoc_log->value() },
4096 		{ "FLDIGI_LOGBOOK_QSL_R", inpQSLrcvddate_log->value() },
4097 		{ "FLDIGI_LOGBOOK_QSL_S", inpQSLsentdate_log->value() },
4098 		{ "FLDIGI_LOGBOOK_TX_PWR", inpTX_pwr_log->value() },
4099 		{ "FLDIGI_LOGBOOK_COUNTY", inpCNTY_log->value() },
4100 		{ "FLDIGI_LOGBOOK_IOTA", inpIOTA_log->value() },
4101 		{ "FLDIGI_LOGBOOK_DXCC", inpDXCC_log->value() },
4102 		{ "FLDIGI_LOGBOOK_QSL_VIA", inpQSL_VIA_log->value() },
4103 		{ "FLDIGI_LOGBOOK_CONTINENT", inpCONT_log->value() },
4104 		{ "FLDIGI_LOGBOOK_CQZ", inpCQZ_log->value() },
4105 		{ "FLDIGI_LOGBOOK_ITUZ", inpITUZ_log->value() },
4106 		{ "FLDIGI_LOGBOOK_SS_SERNO", inp_log_cwss_serno->value() },
4107 		{ "FLDIGI_LOGBOOK_SS_PREC", inp_log_cwss_prec->value() },
4108 		{ "FLDIGI_LOGBOOK_SS_CHK", inp_log_cwss_chk->value() },
4109 		{ "FLDIGI_LOGBOOK_SS_SEC", inp_log_cwss_sec->value() },
4110 		{ "FLDIGI_LOGBOOK_NOTES", inpNotes_log->value() }
4111 
4112 	};
4113 
4114 #ifndef __WOE32__
4115 	// pSKEDH
4116 	static std::string pSKEDh = ScriptsDir;
4117 	pSKEDh.erase(pSKEDh.length()-1,1);
4118 	const char* p;
4119 	if ((p = getenv("pSKEDH")))
4120 		pSKEDh.append(":").append(p);
4121 	env[pSKEDH].val = pSKEDh.c_str();
4122 
4123 	// IPC keys
4124 	char key[2][8];
4125 	snprintf(key[0], sizeof(key[0]), "%d", progdefaults.rx_msgid);
4126 	env[FLDIGI_RX_IPC_KEY].val = key[0];
4127 	snprintf(key[1], sizeof(key[1]), "%d", progdefaults.tx_msgid);
4128 	env[FLDIGI_TX_IPC_KEY].val = key[1];
4129 #endif
4130 
4131 	// pid
4132 	char pid[6];
4133 	snprintf(pid, sizeof(pid), "%d", getpid());
4134 	env[FLDIGI_PID].val = pid;
4135 
4136 	// frequencies
4137 	char dial_freq[20];
4138 	snprintf(dial_freq, sizeof(dial_freq), "%ld", (long)wf->rfcarrier());
4139 	env[FLDIGI_DIAL_FREQUENCY].val = dial_freq;
4140 	char audio_freq[6];
4141 	snprintf(audio_freq, sizeof(audio_freq), "%d", active_modem->get_freq());
4142 	env[FLDIGI_AUDIO_FREQUENCY].val = audio_freq;
4143 	char freq[20];
4144 	snprintf(freq, sizeof(freq), "%ld", (long)(wf->rfcarrier() + (wf->USB()
4145 																  ? active_modem->get_freq()
4146 																  : -active_modem->get_freq())));
4147 	env[FLDIGI_FREQUENCY].val = freq;
4148 
4149 	// debugging vars
4150 #if !defined(NDEBUG) && !defined(__WOE32__)
4151 	unsetenv("FLDIGI_NO_EXEC");
4152 	unsetenv("MALLOC_CHECK_");
4153 	unsetenv("MALLOC_PERTURB_");
4154 #endif
4155 
4156 	string temp;
4157 	size_t pch;
4158 	for (size_t j = 0; j < sizeof(env) / sizeof (*env); j++) {
4159 		temp = env[j].val;
4160 		while ((pch = temp.find("\n")) != string::npos) temp[pch] = ';';
4161 		setenv(env[j].var, temp.c_str(), 1);
4162 	}
4163 
4164 	string path = getenv("PATH");
4165 	string mypath = ScriptsDir;
4166 	if (mypath[mypath.length()-1] == '/')
4167 		mypath.erase(mypath.length()-1, 1);
4168 	mypath.append(":");
4169 	path.insert(0,mypath);
4170 	setenv("PATH", path.c_str(), 1);
4171 
4172 }
4173 
4174 // this is only for the case where the user tries to nest <EXEC>...
4175 // as in
4176 // <EXEC> ... <EXEC> ... </EXEC></EXEC>
4177 // which is not permitted
4178 static void pEND_EXEC(std::string &s, size_t &i, size_t endbracket)
4179 {
4180 	substitute(s, i, endbracket, "");
4181 	return;
4182 }
4183 
4184 #ifndef __MINGW32__
4185 static void pEXEC(std::string &s, size_t &i, size_t endbracket)
4186 {
4187 	if (within_exec) {
4188 		substitute(s, i, endbracket, "");
4189 		return;
4190 	}
4191 
4192 	size_t start = s.find(">", i);
4193 	size_t end = s.find("</EXEC>", start);
4194 
4195 	if (start == std::string::npos ||
4196 		end == std::string::npos) {
4197 		i++;
4198 		return;
4199 	}
4200 
4201 	std::string execstr = s.substr(start+1, end-start-1);
4202 	within_exec = true;
4203 	MACROTEXT m;
4204 	execstr = m.expandMacro(execstr, true);
4205 //	execstr.insert(0,ScriptsDir);
4206 	within_exec = false;
4207 
4208 	int pfd[2];
4209 	if (pipe(pfd) == -1) {
4210 		LOG_PERROR("pipe");
4211 		return;
4212 	}
4213 	pid_t pid;
4214 	switch (pid = fork()) {
4215 		case -1:
4216 			LOG_PERROR("fork");
4217 			return;
4218 		case 0: // child
4219 			close(pfd[0]);
4220 			if (dup2(pfd[1], STDOUT_FILENO) != STDOUT_FILENO) {
4221 				LOG_PERROR("dup2");
4222 				exit(EXIT_FAILURE);
4223 			}
4224 			close(pfd[1]);
4225 			set_macro_env();
4226 			execl("/bin/sh", "sh", "-c", execstr.c_str(), (char *)NULL);
4227 			perror("execl");
4228 			exit(EXIT_FAILURE);
4229 	}
4230 
4231 	// parent
4232 	close(pfd[1]);
4233 
4234 	// give child process time to complete
4235 	MilliSleep(50);
4236 	FILE* fp = fdopen(pfd[0], "r");
4237 	if (!fp) {
4238 		LOG_PERROR("fdopen");
4239 		close(pfd[0]);
4240 		return;
4241 	}
4242 
4243 	s.erase(i, end - i + strlen("</EXEC>"));
4244 
4245 	char ln[BUFSIZ];
4246 	string lnbuff = "";
4247 	while (fgets(ln, sizeof(ln), fp)) {
4248 		lnbuff.append(ln);
4249 	}
4250 	// remove all trailing end-of-lines
4251 	while (lnbuff[lnbuff.length()-1] == '\n')
4252 		lnbuff.erase(lnbuff.length()-1,1);
4253 
4254 	if (!lnbuff.empty()) {
4255 		lnbuff = m.expandMacro(lnbuff, false);
4256 		s.insert(i, lnbuff);
4257 		i += lnbuff.length();
4258 	} else
4259 		i++;
4260 
4261 	fclose(fp);
4262 	close(pfd[0]);
4263 
4264 }
4265 #else // !__MINGW32__
4266 
4267 static void pEXEC(std::string& s, size_t& i, size_t endbracket)
4268 {
4269 	if (within_exec) {
4270 		substitute(s, i, endbracket, "");
4271 		return;
4272 	}
4273 	size_t start, end;
4274 	if ((start = s.find('>', i)) == std::string::npos ||
4275 		(end = s.rfind("</EXEC>")) == std::string::npos) {
4276 		i++;
4277 		return;
4278 	}
4279 	start++;
4280 
4281 	std::string execstr = s.substr(start, end-start);
4282 	within_exec = true;
4283 	MACROTEXT m;
4284 	execstr = m.expandMacro(execstr, true);
4285 	within_exec = false;
4286 
4287 	char* cmd = strdup(execstr.c_str());
4288 
4289 	STARTUPINFO si;
4290 	PROCESS_INFORMATION pi;
4291 	memset(&si, 0, sizeof(si));
4292 	si.cb = sizeof(si);
4293 	memset(&pi, 0, sizeof(pi));
4294 	if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
4295 		LOG_ERROR("CreateProcess failed with error code %ld", GetLastError());
4296 	CloseHandle(pi.hProcess);
4297 	CloseHandle(pi.hThread);
4298 	free(cmd);
4299 
4300 	s.erase(i, end + strlen("</EXEC>") - i);
4301 }
4302 #endif // !__MINGW32__
4303 
4304 static void pEQSL(std::string& s, size_t& i, size_t endbracket)
4305 {
4306 	if (within_exec || progdefaults.eqsl_when_logged) {
4307 		substitute(s, i, endbracket, "");
4308 		return;
4309 	}
4310 	size_t start = s.find(':', i);
4311 
4312 	std::string msg = "";
4313 	if (start != std::string::npos)
4314 		msg = s.substr(start + 1, endbracket-start-1);
4315 
4316 	makeEQSL(msg.c_str());
4317 
4318 	substitute(s, i, endbracket, "");
4319 	return;
4320 }
4321 
4322 static void MAPIT(int how)
4323 {
4324 	float lat = 0, lon = 0;
4325 	std::string sCALL = inpCall->value();
4326 	std::string sLOC = inpLoc->value();
4327 
4328 	std::string url = "http://maps.google.com/maps?q=";
4329 
4330 	if (how > 1 && !lookup_country.empty()) {
4331 		url.append(lookup_addr1).append(",").append(lookup_addr2).append(",");
4332 		url.append(lookup_state).append(",").append(lookup_country);
4333 	} else {
4334 		if (how > 0 && (!lookup_latd.empty() && !lookup_lond.empty())) {
4335 			url.append(lookup_latd).append(",");
4336 			url.append(lookup_lond);
4337 		} else {
4338 			if (sLOC.empty()) return;
4339 			if (sLOC.length() < 4) return;
4340 			if (sLOC.length() < 6) sLOC.append("aa");
4341 			for (size_t i = 0; i < 6; i++) sLOC[i] = toupper(sLOC[i]);
4342 			if (sLOC[0] -'A' > 17 || sLOC[4] - 'A' > 23 ||
4343 				sLOC[1] -'A' > 17 || sLOC[5] - 'A' > 23 ||
4344 				!isdigit(sLOC[2]) || !isdigit(sLOC[3])) return;
4345 			lon =	-180.0 +
4346 			(sLOC[0] - 'A') * 20 +
4347 			(sLOC[2] - '0') * 2 +
4348 			(sLOC[4] - 'A' + 0.5) / 12;
4349 			lat = -90.0 +
4350 			(sLOC[1] - 'A') * 10 +
4351 			(sLOC[3] - '0') +
4352 			(sLOC[5] - 'A' + 0.5) / 24;
4353 			char sdata[20];
4354 			snprintf(sdata, sizeof(sdata),"%10.6f", lat);
4355 			url.append(sdata).append(",");
4356 			snprintf(sdata, sizeof(sdata),"%10.6f", lon);
4357 			url.append(sdata);
4358 		}
4359 	}
4360 	if (!sCALL.empty()) url.append("(").append(sCALL).append(")");
4361 	else url.append("(nocall)");
4362 	url.append("&t=p&z=10");
4363 	cb_mnuVisitURL(NULL, (void*)url.c_str());
4364 }
4365 
4366 static void pMAPIT(std::string &s, size_t &i, size_t endbracket)
4367 {
4368 	if (within_exec) {
4369 		substitute(s, i, endbracket, "");
4370 		return;
4371 	}
4372 	std::string sVal = s.substr(i + 7, endbracket - i - 7);
4373 	if (sVal.length() > 0) {
4374 		if (sVal.compare(0,3,"adr") == 0)
4375 			REQ(MAPIT,2);
4376 		else if (sVal.compare(0,6,"latlon") == 0)
4377 			REQ(MAPIT,1);
4378 		else if (sVal.compare(0,3,"loc") == 0)
4379 			REQ(MAPIT,0);
4380 		else
4381 			REQ(MAPIT,2);
4382 	} else
4383 		REQ(MAPIT,2);
4384 	s.erase(i, s.find('>', i) + 1 - i);
4385 	expand = false;
4386 }
4387 
4388 static void pSTOP(std::string &s, size_t &i, size_t endbracket)
4389 {
4390 	if (within_exec) {
4391 		substitute(s, i, endbracket, "");
4392 		return;
4393 	}
4394 	s.erase(i, s.find('>', i) + 1 - i);
4395 	expand = false;
4396 }
4397 
4398 static void pCONT(std::string &s, size_t &i, size_t endbracket)
4399 {
4400 	if (within_exec) {
4401 		substitute(s, i, endbracket, "");
4402 		return;
4403 	}
4404 	s.erase(i, s.find('>', i) + 1 - i);
4405 	expand = true;
4406 }
4407 
4408 //----------------------------------------------------------------------
4409 // macro scheduling
4410 //----------------------------------------------------------------------
4411 
4412 static long sk_xdt, sk_xtm;
4413 
4414 static void pLOCAL(std::string &s, size_t &i, size_t endbracket)
4415 {
4416 	local_timed_exec = true;
4417 	substitute(s, i, endbracket, "");
4418 }
4419 
4420 static void pSKED(std::string &s, size_t &i, size_t endbracket)
4421 {
4422 	if (within_exec || progStatus.skip_sked_macro) {
4423 		substitute(s, i, endbracket, "");
4424 		return;
4425 	}
4426 	std::string data = s.substr(i+6, endbracket - i - 6);
4427 	size_t p = data.find(":");
4428 	if (p == std::string::npos) {
4429 		exec_date = (local_timed_exec ? ldate() : zdate());
4430 		exec_time = data;
4431 		if (exec_time.empty()) exec_time = (local_timed_exec ? ltime() : ztime());
4432 	} else {
4433 		exec_time = data.substr(0, p);
4434 		exec_date = data.substr(p+1);
4435 	}
4436 	if (exec_time.length() == 4)
4437 		exec_time.append("00");
4438 
4439 	sk_xdt = atol(exec_date.c_str());
4440 	sk_xtm = atol(exec_time.c_str());
4441 
4442 	timed_exec = true;
4443 	substitute(s, i, endbracket, "");
4444 }
4445 
4446 int timed_ptt = -1;
4447 
4448 void do_timed_execute(void *)
4449 {
4450 	long dt, tm;
4451 	dt = atol( local_timed_exec ? ldate() : zdate() );
4452 	tm = atol( local_timed_exec ? ltime() : ztime() );
4453 
4454 	if (dt >= sk_xdt && tm >= sk_xtm) {
4455 		show_clock(false);
4456 		if (timed_ptt != 1) {
4457 			push2talk->set(true);
4458 			timed_ptt = 1;
4459 		}
4460 		Qwait_time = 0;
4461 		start_tx();
4462 		que_ok = true;
4463 		btnMacroTimer->label(0);
4464 		btnMacroTimer->color(FL_BACKGROUND_COLOR);
4465 		btnMacroTimer->set_output();
4466 		sk_xdt = sk_xtm = 0;
4467 	} else {
4468 		show_clock(true);
4469 		if (timed_ptt != 0) {
4470 			push2talk->set(false);
4471 			timed_ptt = 0;
4472 		}
4473 		Fl::repeat_timeout(1.0, do_timed_execute);
4474 	}
4475 }
4476 
4477 static void doSKED(std::string s)
4478 {
4479 	size_t p = s.find(":");
4480 	if (p == std::string::npos) {
4481 		exec_date = (local_timed_exec ? ldate() : zdate());
4482 		exec_time = s;
4483 		if (exec_time.empty())
4484 			exec_time = (local_timed_exec ? ltime() : ztime());
4485 	} else {
4486 		exec_time = s.substr(0, p);
4487 		exec_date = s.substr(p+1);
4488 	}
4489 	if (exec_time.length() == 4)
4490 		exec_time.append("00");
4491 
4492 	string txt;
4493 	txt.assign("Next scheduled transmission at ").
4494 		append(exec_time.substr(0,2)).append(":").
4495 		append(exec_time.substr(2,2)).append(":").
4496 		append(exec_time.substr(4,2)).
4497 		append(", on ").
4498 		append(exec_date.substr(0,4)).append("/").
4499 		append(exec_date.substr(4,2)).append("/").
4500 		append(exec_date.substr(6,2)).append("\n");
4501 	btnMacroTimer->label("SKED");
4502 	btnMacroTimer->color(fl_rgb_color(240, 240, 0));
4503 	btnMacroTimer->redraw_label();
4504 	ReceiveText->addstr(txt, FTextBase::CTRL);
4505 
4506 	Qwait_time = 9999;
4507 
4508 	sk_xdt = atol(exec_date.c_str());
4509 	sk_xtm = atol(exec_time.c_str());
4510 
4511 	Fl::add_timeout(0.0, do_timed_execute);
4512 
4513 }
4514 
4515 static void pTxQueSKED(std::string &s, size_t &i, size_t endbracket)
4516 {
4517 	if (within_exec) {
4518 		substitute(s, i, endbracket, "");
4519 		return;
4520 	}
4521 	struct CMDS cmd = { s.substr(i + 7, endbracket - i - 7), doSKED };
4522 	push_txcmd(cmd);
4523 	substitute(s, i, endbracket, "^!");
4524 }
4525 
4526 static void pUNTIL(std::string &s, size_t &i, size_t endbracket)
4527 {
4528 	if (within_exec) {
4529 		substitute(s, i, endbracket, "");
4530 		return;
4531 	}
4532 	std::string data = s.substr(i+7, endbracket - i - 7);
4533 	size_t p = data.find(":");
4534 	if (p == std::string::npos) {
4535 		until_date = (local_timed_exec ? ldate() : zdate());
4536 		until_time = data;
4537 	} else {
4538 		until_time = data.substr(0, p);
4539 		until_date = data.substr(p+1);
4540 	}
4541 	if (until_time.empty()) {
4542 		substitute(s, i, endbracket, "");
4543 		return;
4544 	}
4545 	if (until_time.length() == 4) until_time.append("00");
4546 	run_until = true;
4547 	substitute(s, i, endbracket, "");
4548 }
4549 
4550 void queue_reset()
4551 {
4552 	if (!Tx_cmds.empty()) {
4553 		Fl::remove_timeout(post_queue_execute);
4554 		Fl::remove_timeout(queue_execute_after_rx);
4555 		Fl::remove_timeout(doneIDLE);
4556 		Fl::remove_timeout(doneWAIT);
4557 		while (!Tx_cmds.empty()) Tx_cmds.pop();
4558 	}
4559 	while (!Rx_cmds.empty()) Rx_cmds.pop();
4560 	while (!mf_stack.empty()) mf_stack.pop();
4561 	Qwait_time = 0;
4562 	Qidle_time = 0;
4563 	que_ok = true;
4564 	run_until = false;
4565 	tx_queue_done = true;
4566 	progStatus.skip_sked_macro = false;
4567 }
4568 
4569 // execute an in-line macro tag
4570 // occurs during the Tx state
4571 void Tx_queue_execute()
4572 {
4573 	if (Tx_cmds.empty()) {
4574 		Qwait_time = 0;
4575 		Qidle_time = 0;
4576 		tx_queue_done = true;
4577 		return;
4578 	}
4579 	CMDS cmd = Tx_cmds.front();
4580 	Tx_cmds.pop();
4581 	LOG_INFO("%s", cmd.cmd.c_str());
4582 	REQ(postQueue, cmd.cmd);
4583 	cmd.fp(cmd.cmd);
4584 	return;
4585 }
4586 
4587 bool queue_must_rx()
4588 {
4589 // return true if current command is not a member 'must_rx'
4590 	static std::string must_rx = "<!MOD<!WAI<!GOH<!QSY<!GOF<!RIG<!FIL<!PUS<!POP";//<!DIG<!FRE";
4591 	if (Tx_cmds.empty()) return false;
4592 	CMDS cmd = Tx_cmds.front();
4593 	return (must_rx.find(cmd.cmd.substr(0,5)) != std::string::npos);
4594 }
4595 
4596 // execute all post Tx macros in the Rx_cmds queu
4597 // occurs immediately after the ^r execution
4598 // AND after TX_STATE returns to Rx
4599 // ^r is the control string substitute for the <RX> macro tag
4600 int time_out = 400;
4601 void Rx_queue_execution(void *)
4602 {
4603 	if (rx_tune_on) {
4604 		Fl::repeat_timeout( .050, Rx_queue_execution );
4605 		return;
4606 	}
4607 
4608 	if (!Tx_cmds.empty()) {
4609 		Fl::remove_timeout(post_queue_execute);
4610 		Fl::remove_timeout(queue_execute_after_rx);
4611 		Fl::remove_timeout(doneIDLE);
4612 		Fl::remove_timeout(doneWAIT);
4613 		while (!Tx_cmds.empty()) Tx_cmds.pop();
4614 	}
4615 
4616 	if (trx_state != STATE_RX) {
4617 		if (time_out-- == 0) {
4618 			while (!Rx_cmds.empty()) Rx_cmds.pop();
4619 			LOG_ERROR("%s", "failed");
4620 			time_out = 200;
4621 			return;
4622 		}
4623 		Fl::repeat_timeout( .050, Rx_queue_execution );
4624 		return;
4625 	}
4626 	LOG_INFO("action delayed by %4.2f seconds", (400 - time_out)*.050);
4627 
4628 	time_out = 400;
4629 	CMDS cmd;
4630 	while (!Rx_cmds.empty()) {
4631 		cmd = Rx_cmds.front();
4632 		Rx_cmds.pop();
4633 		LOG_INFO("%s", cmd.cmd.c_str());
4634 		REQ(postQueue, cmd.cmd);
4635 		cmd.cmd.erase(0,2);
4636 		cmd.cmd.insert(0,"<!");
4637 		cmd.fp(cmd.cmd);
4638 		Fl::awake();
4639 		if (rx_tune_on) {
4640 			Fl::repeat_timeout( .050, Rx_queue_execution );
4641 			return;
4642 		}
4643 		if (macro_rx_wait) return;
4644 	}
4645 	return;
4646 }
4647 
4648 void Rx_queue_execute()
4649 {
4650 	if (Rx_cmds.empty()) return;
4651 	Fl::add_timeout(0, Rx_queue_execution);
4652 }
4653 
4654 void rx_que_continue(void *)
4655 {
4656 	macro_rx_wait = false;
4657 	Rx_queue_execute();
4658 }
4659 
4660 struct MTAGS { const char *mTAG; void (*fp)(std::string &, size_t&, size_t );};
4661 
4662 static const MTAGS mtags[] = {
4663 	{"<CPS_FILE:",	pCPS_FILE},
4664 	{"<CPS_N:",		pCPS_N},
4665 	{"<CPS_STRING:",pCPS_STRING},
4666 	{"<CPS_TEST",	pCPS_TEST},
4667 
4668 	{"<WAV_FILE:",	pWAV_FILE},
4669 	{"<WAV_N:",		pWAV_N},
4670 	{"<WAV_STRING:",pWAV_STRING},
4671 	{"<WAV_TEST",	pWAV_TEST},
4672 
4673 	{"<COMMENT:",	pCOMMENT},
4674 	{"<#",			pCOMMENT},
4675 	{"<CALL>",		pCALL},
4676 	{"<FREQ>",		pFREQ},
4677 	{"<BAND>",		pBAND},
4678 	{"<LOC>",		pLOC},
4679 	{"<MODE>",		pMODE},
4680 	{"<NAME>",		pNAME},
4681 	{"<QTH>",		pQTH},
4682 	{"<RST>",		pRST},
4683 	{"<ST>",		pST},
4684 	{"<PR>",		pPR},
4685 	{"<MYCALL>",	pMYCALL},
4686 	{"<MYLOC>",		pMYLOC},
4687 	{"<MYNAME>",	pMYNAME},
4688 	{"<MYQTH>",		pMYQTH},
4689 	{"<MYRST>",		pMYRST},
4690 	{"<MYCLASS>",	pMYCLASS},
4691 	{"<MYSECTION>",	pMYSECTION},
4692 	{"<MYSTATE>",	pMYSTATE},
4693 	{"<MYST>",		pMYST},
4694 	{"<MYCOUNTY>",	pMYCOUNTY},
4695 	{"<MYCNTY>",	pMYCNTY},
4696 	{"<ANTENNA>",	pANTENNA},
4697 	{"<QSOTIME>",	pQSOTIME},
4698 	{"<QSONBR>",	pQSONBR},
4699 	{"<NXTNBR>",	pNXTNBR},
4700 	{"<INFO1>",		pINFO1},
4701 	{"<INFO2>",		pINFO2},
4702 	{"<LDT>",		pLDT},
4703 	{"<LDT:",		pLDT},
4704 	{"<ILDT",		pILDT},
4705 	{"<ZDT>",		pZDT},
4706 	{"<ZDT:",		pZDT},
4707 	{"<IZDT",		pIZDT},
4708 	{"<LT",			pLT},
4709 	{"<ZT",			pZT},
4710 	{"<LD>",		pLD},
4711 	{"<LD:",		pLD},
4712 	{"<ZD>",		pZD},
4713 	{"<ZD:",		pZD},
4714 	{"<ID>",		p_ID},
4715 	{"<TEXT>",		pTEXT},
4716 	{"<VIDEO:",		pVIDEO},
4717 	{"<CWID>",		pCWID},
4718 	{"<VER>",		pVER},
4719 	{"<RIGCAT:",	pRIGCAT},
4720 	{"<FLRIG:",		pFLRIG},
4721 	{"<CNTR>",		pCNTR},
4722 	{"<DECR>",		pDECR},
4723 	{"<INCR>",		pINCR},
4724 	{"<X1>",		pXOUT},
4725 	{"<XIN>",		pXIN},
4726 	{"<XOUT>",		pXOUT},
4727 	{"<FDCLASS>",	pFD_CLASS},
4728 	{"<FDSECT>",	pFD_SECTION},
4729 	{"<CLASS>",		pCLASS},
4730 	{"<SECTION>",	pSECTION},
4731 	{"<XBEG>",		pXBEG},
4732 	{"<XEND>",		pXEND},
4733 	{"<SAVEXCHG>",	pSAVEXCHG},
4734 	{"<SERNO>",		pSERNO},
4735 	{"<LASTNO>",	pLASTNO},
4736 	{"<LOG",		pLOG},
4737 	{"<LNW",		pLNW},
4738 	{"<CLRLOG>",	pCLRLOG},
4739 	{"<EQSL",		pEQSL},
4740 	{"<TIMER:",		pTIMER},
4741 	{"<AFTER:",		pAFTER},
4742 	{"<IDLE:",		pIDLE},
4743 	{"<TUNE:",		pTUNE},
4744 	{"<WAIT:",		pWAIT},
4745 	{"<NRSID:",		pNRSID},
4746 	{"<MODEM>",		pMODEM_COMPSKED},
4747 	{"<MODEM:",		pMODEM},
4748 	{"<EXEC>",		pEXEC},
4749 	{"</EXEC>",		pEND_EXEC},
4750 	{"<STOP>",		pSTOP},
4751 	{"<CONT>",		pCONT},
4752 	{"<PAUSE>",		pPAUSE},
4753 	{"<GET>",		pGET},
4754 	{"<CLRRX>",		pCLRRX},
4755 	{"<CLRTX>",		pCLRTX},
4756 	{"<CLRQSO>",	pCLRQSO},
4757 	{"<FOCUS>",		pFOCUS},
4758 	{"<QSY+:",		pQSYPLUS},
4759 	{"<FILE:",		pFILE},
4760 	{"<WPM:",		pWPM},
4761 	{"<RISE:",		pRISETIME},
4762 	{"<PRE:",		pPRE},
4763 	{"<POST:",		pPOST},
4764 	{"<AFC:",		pAFC},
4765 	{"<LOCK:",		pLOCK},
4766 	{"<REV:",		pREV},
4767 	{"<HS:",		pHS},
4768 	{"<RXRSID:",	pRX_RSID},
4769 	{"<TXRSID:",	pTX_RSID},
4770 	{"<DTMF:",		pDTMF},
4771 	{"<SRCHUP>",	pSRCHUP},
4772 	{"<SRCHDN>",	pSRCHDN},
4773 	{"<GOHOME>",	pGOHOME},
4774 	{"<GOFREQ:",	pGOFREQ},
4775 	{"<QRG:",		pQRG},
4776 	{"<QSY:",		pQSY},
4777 	{"<QSYTO>",		pQSYTO},
4778 	{"<QSYFM>",		pQSYFM},
4779 	{"<RIGMODE:",	pRIGMODE},
4780 	{"<FILWID:",	pFILWID},
4781 	{"<RIGHI:",     pRIGHI},
4782 	{"<RIGLO:",     pRIGLO},
4783 	{"<MAPIT:",		pMAPIT},
4784 	{"<MAPIT>",		pMAPIT},
4785 	{"<REPEAT>",	pREPEAT},
4786 	{"<SKED:",		pSKED},
4787 	{"<UNTIL:",		pUNTIL},
4788 	{"<LOCAL>",		pLOCAL},
4789 	{"<TXATTEN:",	pTXATTEN},
4790 	{"<POP>",		pPOP},
4791 	{"<PUSH",		pPUSH},
4792 	{"<DIGI>",		pDIGI},
4793 	{"<ALERT:",		pALERT},
4794 	{"<AUDIO:",		pAUDIO},
4795 	{"<BUFFERED>",	pBUFFERED},
4796 #ifdef __WIN32__
4797 	{"<TALK:",		pTALK},
4798 #endif
4799 	{"<CSV:",		pCSV},
4800 	{"<WX>",		pWX},
4801 	{"<WX:",		pWX2},
4802 	{"<IMAGE:",		pTxQueIMAGE},
4803 	{"<AVATAR>",	pTxQueAVATAR},
4804 // Tx Delayed action
4805 	{"<!WPM:",		pTxQueWPM},
4806 	{"<!RISE:",		pTxQueRISETIME},
4807 	{"<!PRE:",		pTxQuePRE},
4808 	{"<!POST:",		pTxQuePOST},
4809 	{"<!GOHOME>",	pTxQueGOHOME},
4810 	{"<!GOFREQ:",	pTxQueGOFREQ},
4811 	{"<!QSY:",		pTxQueQSY},
4812 	{"<!IDLE:",		pTxQueIDLE},
4813 	{"<!WAIT:",		pTxQueWAIT},
4814 	{"<!SKED:",		pTxQueSKED},
4815 	{"<!MODEM:",	pTxQueMODEM},
4816 	{"<!RIGMODE:",	pTxQueRIGMODE},
4817 	{"<!FILWID:",	pTxQueFILWID},
4818     {"<!RIGHI:",    pTxQueRIGHI},
4819     {"<!RIGLO:",    pTxQueRIGLO},
4820 	{"<!TXATTEN:",	pTxQueTXATTEN},
4821 	{"<!RIGCAT:",	pTxQueRIGCAT},
4822 	{"<!FLRIG:",	pTxQueFLRIG},
4823 	{"<!PUSH",		pTxQuePUSH},
4824 	{"<!POP>",		pTxQuePOP},
4825 	{"<!DIGI>",		pTxDIGI},
4826 	{"<!FREQ>",		pTxFREQ},
4827 	{"<!TUNE:",		pTxQueTUNE},
4828 
4829 // Rx After action
4830 	{"<@MODEM:",	pRxQueMODEM},
4831 	{"<@RIGCAT:",	pRxQueRIGCAT},
4832 	{"<@FLRIG:",	pRxQueFLRIG},
4833 	{"<@GOFREQ:",	pRxQueGOFREQ},
4834 	{"<@GOHOME>",	pRxQueGOHOME},
4835 	{"<@RIGMODE:",	pRxQueRIGMODE},
4836 	{"<@FILWID:",	pRxQueFILWID},
4837     {"<@RIGHI:",    pRxQueRIGHI},
4838     {"<@RIGLO:",    pRxQueRIGLO},
4839 	{"<@TXRSID:",	pRxQueTXRSID},
4840 	{"<@LOCK:",		pRxQueLOCK},
4841 	{"<@WAIT:",     pRxQueWAIT},
4842 	{"<@TUNE:",		pRxQueTUNE},
4843 	{"<@PUSH",		pRxQuePUSH},
4844 	{"<@POP>",		pRxQuePOP},
4845 
4846 	{"<RX>",		pRX},
4847 	{"<TX>",		pTX},
4848 	{"<TX/RX>",		pTXRX},
4849 
4850 	{0, 0}
4851 };
4852 
4853 int MACROTEXT::loadMacros(const std::string& filename)
4854 {
4855 	std::string mLine;
4856 	std::string mName;
4857 	std::string mDef;
4858 	int    mNumber = 0;
4859 	size_t crlf, idx;
4860 	char   szLine[4096];
4861 	bool   convert = false;
4862 
4863 	ifstream mFile(filename.c_str());
4864 
4865 	if (!mFile) {
4866 		create_new_macros();
4867 	} else {
4868 
4869 		mFile.getline(szLine, 4095);
4870 		mLine = szLine;
4871 		if (mLine.find("//fldigi macro definition file") != 0) {
4872 			mFile.close();
4873 			return -2;
4874 		}
4875 		if (mLine.find("extended") == std::string::npos) {
4876 			convert = true;
4877 			changed = true;
4878 		}
4879 		// clear all of the macros
4880 		for (int i = 0; i < MAXMACROS; i++) {
4881 			name[i] = "";
4882 			text[i] = "";
4883 		}
4884 		while (!mFile.eof()) {
4885 			mFile.getline(szLine,4095);
4886 			mLine = szLine;
4887 			if (!mLine.length())
4888 				continue;
4889 			if (mLine.find("//") == 0) // skip over all comment lines
4890 				continue;
4891 			if (mLine.find("/$") == 0) {
4892 				idx = mLine.find(" ", 3);
4893 				if (idx != std::string::npos) {
4894 					mNumber = atoi(&mLine[3]);
4895 					if (mNumber < 0 || mNumber > (MAXMACROS - 1))
4896 						break;
4897 					if (convert && mNumber > 9) mNumber += 2;
4898 					name[mNumber] = mLine.substr(idx+1);
4899 				}
4900 				continue;
4901 			}
4902 			crlf = mLine.rfind("\\n");
4903 			if (crlf != std::string::npos) {
4904 				mLine.erase(crlf);
4905 				mLine.append("\n");
4906 			}
4907 			text[mNumber] = text[mNumber] + mLine;
4908 		}
4909 		mFile.close();
4910 	}
4911 
4912 	return 0;
4913 }
4914 
4915 void MACROTEXT::loadDefault()
4916 {
4917 	int erc;
4918 	std::string Filename = MacrosDir;
4919 	Filename.append("macros.mdf");
4920 	LOG_INFO("macro file name: %s", progStatus.LastMacroFile.c_str());
4921 	if (progdefaults.UseLastMacro == true) {
4922 		if (progStatus.LastMacroFile.find("/") != string::npos ||
4923 			progStatus.LastMacroFile.find("\\") != string::npos)
4924 			Filename.assign(progStatus.LastMacroFile);
4925 		else
4926 			Filename.assign(MacrosDir).append(progStatus.LastMacroFile);
4927 	}
4928 	LOG_INFO("loading: %s", Filename.c_str());
4929 	progStatus.LastMacroFile = Filename;
4930 
4931 	if ((erc = loadMacros(Filename)) != 0)
4932 #ifndef __WOE32__
4933 		LOG_ERROR("Error #%d loading %s\n", erc, Filename.c_str());
4934 #else
4935 	;
4936 #endif
4937 	showMacroSet();
4938 	if (progdefaults.DisplayMacroFilename) {
4939 		LOG_INFO("%s", progStatus.LastMacroFile.c_str());
4940 		string Macroset;
4941 		Macroset.assign("\
4942 \n================================================\n\
4943 Read macros from: ").append(progStatus.LastMacroFile).append("\
4944 \n================================================\n");
4945 #ifdef __WOE32__
4946 		size_t p = string::npos;
4947 		while ( (p = Macroset.find("/")) != string::npos)
4948 			Macroset[p] = '\\';
4949 #endif
4950 		if (active_modem->get_mode() == MODE_IFKP)
4951 			ifkp_rx_text->addstr(Macroset);
4952 		else if (active_modem->get_mode() == MODE_FSQ)
4953 			fsq_rx_text->addstr(Macroset);
4954 		else
4955 			ReceiveText->addstr(Macroset);
4956 	}
4957 }
4958 
4959 void MACROTEXT::openMacroFile()
4960 {
4961 	std::string deffilename = MacrosDir;
4962 
4963 	if (progStatus.LastMacroFile.find("/") != string::npos ||
4964 		progStatus.LastMacroFile.find("\\") != string::npos)
4965 		deffilename.assign(progStatus.LastMacroFile);
4966 	else
4967 		deffilename.append(progStatus.LastMacroFile);
4968 
4969 	const char *p = FSEL::select(
4970 								 _("Open macro file"),
4971 								 _("Fldigi macro definition file\t*.{mdf}"),
4972 								 deffilename.c_str());
4973 	if (p && *p) {
4974 		loadMacros(p);
4975 		progStatus.LastMacroFile = p;
4976 		showMacroSet();
4977 		if (progdefaults.DisplayMacroFilename) {
4978 			string Macroset;
4979 			Macroset.assign("\nLoaded macros: ").append(progStatus.LastMacroFile).append("\n");
4980 			if (active_modem->get_mode() == MODE_IFKP)
4981 				ifkp_rx_text->addstr(Macroset);
4982 			if (active_modem->get_mode() == MODE_FSQ)
4983 				fsq_rx_text->addstr(Macroset);
4984 			else
4985 				ReceiveText->addstr(Macroset);
4986 		}
4987 	}
4988 }
4989 
4990 void MACROTEXT::writeMacroFile()
4991 {
4992 	std::string deffilename = MacrosDir;
4993 	if (progStatus.LastMacroFile.find("/") != string::npos ||
4994 		progStatus.LastMacroFile.find("\\") != string::npos)
4995 		deffilename.assign(progStatus.LastMacroFile);
4996 	else
4997 		deffilename.append(progStatus.LastMacroFile);
4998 
4999 	saveMacros(deffilename.c_str());
5000 }
5001 
5002 void MACROTEXT::saveMacroFile()
5003 {
5004 	std::string deffilename = MacrosDir;
5005 
5006 	if (progStatus.LastMacroFile.find("/") != string::npos ||
5007 		progStatus.LastMacroFile.find("\\") != string::npos)
5008 		deffilename.assign(progStatus.LastMacroFile);
5009 	else
5010 		deffilename.append(progStatus.LastMacroFile);
5011 
5012 	const char *p = FSEL::saveas(
5013 								 _("Save macro file"),
5014 								 _("Fldigi macro definition file\t*.{mdf}"),
5015 								 deffilename.c_str());
5016 	if (!p) return;
5017 	if (!*p) return;
5018 
5019 	string sp = p;
5020 	if (sp.empty()) return;
5021 	if (sp.rfind(".mdf") == string::npos) sp.append(".mdf");
5022 	saveMacros(sp.c_str());
5023 	progStatus.LastMacroFile = sp;
5024 
5025 }
5026 
5027 void MACROTEXT::savecurrentMACROS(std::string &s, size_t &i, size_t endbracket)
5028 {
5029 	writeMacroFile();
5030 	substitute(s, i, endbracket, "");
5031 }
5032 
5033 void MACROTEXT::loadnewMACROS(std::string &s, size_t &i, size_t endbracket)
5034 {
5035 	std::string fname = s.substr(i+8, endbracket - i - 8);
5036 	if (fname.length() > 0) {
5037 		loadMacros(fname);
5038 		progStatus.LastMacroFile = fl_filename_name(fname.c_str());
5039 	}
5040 	substitute(s, i, endbracket, "");
5041 	showMacroSet();
5042 }
5043 
5044 std::string MACROTEXT::expandMacro(std::string &s, bool recurse = false)
5045 {
5046 	size_t idx = 0;
5047 	expand = true;
5048 	buffered = false;
5049 	if (!recurse || rx_only) {
5050 		TransmitON = false;
5051 		ToggleTXRX = false;
5052 	}
5053 	expanded = s;
5054 	const MTAGS *pMtags;
5055 
5056 	xbeg = xend = -1;
5057 	save_xchg = false;
5058 	progStatus.repeatMacro = -1;
5059 	text2repeat.clear();
5060 	idleTime = 0;
5061 	waitTime = 0;
5062 //	tuneTime = 0;
5063 
5064 	while ((idx = expanded.find('<', idx)) != std::string::npos) {
5065 		size_t endbracket = expanded.find('>',idx);
5066 		if (ufind(expanded, "<SAVE", idx) == idx) {
5067 			savecurrentMACROS(expanded, idx, endbracket);
5068 			idx++;
5069 			continue;
5070 		}
5071 		if (ufind(expanded, "<MACROS:",idx) == idx) {
5072 			loadnewMACROS(expanded, idx, endbracket);
5073 			idx++;
5074 			continue;
5075 		}
5076 		// we must handle this specially
5077 		if (ufind(expanded, "<CONT>", idx) == idx)
5078 			pCONT(expanded, idx, endbracket);
5079 		if (!expand) {
5080 			idx++;
5081 			continue;
5082 		}
5083 
5084 		pMtags = mtags;
5085 		while (pMtags->mTAG != 0) {
5086 			if (ufind(expanded, pMtags->mTAG, idx) == idx) {
5087 				pMtags->fp(expanded,idx, endbracket);
5088 				break;
5089 			}
5090 			pMtags++;
5091 		}
5092 		if (pMtags->mTAG == 0)
5093 			idx++;
5094 	}
5095 	if (GET) {
5096 		size_t pos1 = ufind(expanded, "$NAME");
5097 		size_t pos2 = ufind(expanded, "$QTH");
5098 		size_t pos3 = ufind(expanded, "$LOC");
5099 		if (pos1 != std::string::npos && pos2 != std::string::npos) {
5100 			pos1 += 5;
5101 			inpName->value(expanded.substr(pos1, pos2 - pos1).c_str());
5102 		}
5103 		if (pos2 != std::string::npos) {
5104 			pos2 += 4;
5105 			inpQth->value(expanded.substr(pos2, pos3 - pos2).c_str());
5106 		}
5107 		if (pos3 != std::string::npos) {
5108 			pos3 += 4;
5109 			inpLoc->value(expanded.substr(pos3).c_str());
5110 		}
5111 		GET = false;
5112 		return "";
5113 	}
5114 
5115 	if (xbeg != std::string::npos && xend != std::string::npos && xend > xbeg) {
5116 		qso_exchange = expanded.substr(xbeg, xend - xbeg);
5117 	} else if (save_xchg) {
5118 		qso_exchange = expanded;
5119 		save_xchg = false;
5120 	}
5121 
5122 	// force "^r" to be last tag in the expanded std::string
5123 	if ((idx = expanded.find("^r")) != std::string::npos) {
5124 		expanded.erase(idx, 2);
5125 		expanded.append("^r");
5126 	}
5127 
5128 	if (!TransmitON && !Rx_cmds.empty())
5129 		Fl::add_timeout(0, rx_que_continue);
5130 
5131 	return expanded;
5132 }
5133 
5134 void idleTimer(void *)
5135 {
5136 	macro_idle_on = false;
5137 }
5138 
5139 static void finishWait(void *)
5140 {
5141 	if (rx_only) {
5142 		TransmitON = false;
5143 		return;
5144 	}
5145 	if ( TransmitON ) {
5146 		active_modem->set_stopflag(false);
5147 		if (macro_idle_on && idleTime > 0)
5148 			Fl::add_timeout(idleTime, idleTimer);
5149 		start_tx();
5150 		TransmitON = false;
5151 	}
5152 }
5153 
5154 static void set_button(Fl_Button* button, bool value)
5155 {
5156 	button->value(value);
5157 	button->do_callback();
5158 }
5159 
5160 void MACROTEXT::timed_execute()
5161 {
5162 	queue_reset();
5163 	if (active_modem->get_mode() == MODE_IFKP)
5164 		ifkp_tx_text->clear();
5165 	else if (active_modem->get_mode() == MODE_FSQ)
5166 		fsq_tx_text->clear();
5167 	else
5168 		TransmitText->clear();
5169 	if (!rx_only) {
5170 		text2send = expandMacro(exec_string);
5171 		progStatus.skip_sked_macro = true;
5172 		if (active_modem->get_mode() == MODE_IFKP)
5173 			ifkp_tx_text->add_text(text2send);
5174 		else if (active_modem->get_mode() == MODE_FSQ)
5175 			fsq_tx_text->add_text( text2send );
5176 		else
5177 			add_text(text2send);
5178 		exec_string.clear();
5179 		active_modem->set_stopflag(false);
5180 		start_tx();
5181 	}
5182 }
5183 
5184 void MACROTEXT::execute(int n)
5185 {
5186 	guard_lock exec(&exec_mutex);
5187 
5188 	std::string dd, dt;
5189 	dd = (local_timed_exec ? ldate() : zdate());
5190 	dt = (local_timed_exec ? ltime() : ldate());
5191 
5192 	if (run_until && dd >= until_date && dt >= until_time) {
5193 		stopMacroTimer();
5194 		queue_reset();
5195 		return;
5196 	}
5197 
5198 	mNbr = n;
5199 	text2send = expandMacro(text[n]);
5200 
5201 	if (timed_exec && !progStatus.skip_sked_macro) {
5202 		progStatus.repeatMacro = -1;
5203 		exec_string = text[n];
5204 		startTimedExecute(name[n]);
5205 		timed_exec = false;
5206 		return;
5207 	}
5208 
5209 	trx_mode mode = active_modem->get_mode();
5210 
5211 	if (!rx_only) {
5212 		if (progStatus.repeatMacro == -1) {
5213 			if (mode == MODE_IFKP)
5214 				ifkp_tx_text->add_text( text2send );
5215 			else if (mode == MODE_FSQ)
5216 				fsq_tx_text->add_text( text2send );
5217 			else
5218 				add_text( text2send );
5219 		} else {
5220 			size_t p = std::string::npos;
5221 			text2send = text[n];
5222 			while ((p = text2send.find('<')) != std::string::npos)
5223 				text2send[p] = '[';
5224 			while ((p = text2send.find('>')) != std::string::npos)
5225 				text2send[p] = ']';
5226 			if (mode == MODE_IFKP)
5227 				ifkp_tx_text->add_text( text2send );
5228 			else if (mode == MODE_FSQ)
5229 				fsq_tx_text->add_text( text2send );
5230 			else
5231 				add_text( text2send );
5232 		}
5233 	}
5234 	bool keep_tx = !text2send.empty();
5235 	text2send.clear();
5236 
5237 	if (tune_on) {
5238 		if (tune_timeout > 0) {
5239 			Fl::add_timeout(tune_timeout, end_tune, (void *)keep_tx);
5240 			tune_timeout = 0;
5241 		}
5242 		return;
5243 	}
5244 
5245 	if (ToggleTXRX) {
5246 		text2send.clear();
5247 		if (!wf->xmtrcv->value()) {
5248 			REQ(set_button, wf->xmtrcv, true);
5249 			if (macro_idle_on && idleTime > 0)
5250 				Fl::add_timeout(idleTime, idleTimer);
5251 		} else
5252 			REQ(set_button, wf->xmtrcv, false);
5253 		return;
5254 	}
5255 	if (useWait && waitTime > 0) {
5256 		Fl::add_timeout(waitTime, finishWait);
5257 		useWait = false;
5258 		return;
5259 	}
5260 	if ( TransmitON ) {
5261 		if (macro_idle_on && idleTime > 0)
5262 			Fl::add_timeout(idleTime, idleTimer);
5263 		active_modem->set_stopflag(false);
5264 		start_tx();
5265 		TransmitON = false;
5266 	}
5267 }
5268 
5269 void MACROTEXT::repeat(int n)
5270 {
5271 	expandMacro(text[n]);
5272 	progStatus.skip_sked_macro = true;
5273 	LOG_WARN("%s",text2repeat.c_str());
5274 	macro_idle_on = false;
5275 	if (idleTime) progStatus.repeatIdleTime = idleTime;
5276 }
5277 
5278 MACROTEXT::MACROTEXT()
5279 {
5280 	changed = false;
5281 	char szname[5];
5282 	for (int i = 0; i < MAXMACROS; i++) {
5283 		snprintf(szname, sizeof(szname), "F-%d", i+1);
5284 		name[i] = szname;//"";
5285 		text[i] = "";
5286 	}
5287 }
5288 
5289 
5290 static std::string mtext =
5291 "//fldigi macro definition file extended\n\
5292 // This file defines the macro structure(s) for the digital modem program, fldigi\n\
5293 // It also serves as a basis for any macros that are written by the user\n\
5294 //\n\
5295 // The top line of this file should always be the first line in every macro \n\
5296 // definition file (.mdf) for the fldigi program to recognize it as such.\n\
5297 //\n\
5298 ";
5299 
5300 void MACROTEXT::saveMacros(const std::string& fname) {
5301 
5302 	std::string work;
5303 	std::string output;
5304 	char temp[200];
5305 	output.assign(mtext);
5306 	for (int i = 0; i < MAXMACROS; i++) {
5307 		snprintf(temp, sizeof(temp), "\n//\n// Macro # %d\n/$ %d %s\n",
5308 				 i+1, i, macros.name[i].c_str());
5309 		output.append(temp);
5310 		work = macros.text[i];
5311 		size_t pos;
5312 		pos = work.find('\n');
5313 		while (pos != std::string::npos) {
5314 			work.insert(pos, "\\n");
5315 			pos = work.find('\n', pos + 3);
5316 		}
5317 		output.append(work).append("\n");
5318 	}
5319 	UTF8_writefile(fname.c_str(), output);
5320 
5321 	changed = false;
5322 }
5323 
5324