1 /** **************************************************************
2  \page script_parsing Script Parsing Class
3 
4  \par script_parsing.cxx (FLAMP)
5 
6  \par Author(s):
7  Robert Stiles, KK5VD, Copyright © 2014
8  <br>
9  <br>
10  This is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 3 of the License, or
13  (at your option) any later version. This software is distributed in
14  the hope that it will be useful, but WITHOUT ANY WARRANTY; without
15  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16  PURPOSE.  See the GNU General Public License for more details. You
17  should have received a copy of the GNU General Public License
18  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  <br>
20  <br>
21 
22  \par USE:
23  Create a structure of data contained within the SCRIPT_COMMANDS
24  structure \(see \ref script_cmds\).  Each element are as follows.<br>
25 
26  \verbatim
27  { SCRIPT_COMMAND, 0, "AUTO LOAD QUEUE",  1, {0}, { p_string }, 0, 0, 0, 0 }
28  \endverbatim
29 
30  \par "int flags <SCRIPT_COMMAND and/or QUEUE_COMMAND>"
31  Indicates if the script command is a global assignment (SCRIPT_COMMAND)
32  or local for each of the queued files (QUEUE_COMMAND).
33 
34  \par "size_t command_length"
35  Programmer assignment not required. Internally Assigned.  This is the
36  length of the script command in bytes.
37 
38  \par "char command[MAX_COMMAND_LENGTH]"
39  The script command used to match within the content of the script
40  file. MAX_COMMAND_LENGTH is the maximum length of the supplied buffer.
41 
42  \par "int  argc"
43  The number of argument as required for the script command. The
44  parsing engine will limit the number of scanned parameters even of more
45  has been supplied.
46 
47  \par "char *args[MAX_CMD_PARAMETERS]"
48  An array of char string pointers to each parameter scanned for
49  the specific command. MAX_CMD_PARAMETERS is the maximum number
50  of positions available in the parameter buffer table.
51 
52  \par "enum paramter_types param_check[MAX_CMD_PARAMETERS]"
53  A list of validation flags for each parameters required.
54  MAX_CMD_PARAMETERS is the maximum number of positions available
55  in the param_check buffer table. See \ref paramter_types for a list
56  of valid values.<br>
57 
58  \par "calling_func func"
59  When a command string is matched in the script file, this StringParsing
60  Class member is executed for further processing of the command
61  paramaters. This can include any validation checks if required. The member
62  functions are assigned during the creation of the class instance. See the
63  constructor member ScriptParsing::ScriptParsing() for details.
64 
65  \par "const char **valid_values"
66  List of valid paramters. Use function
67  int assign_valid_parameters(const char *command, const char **array, const int array_count);
68  to asssign the values to a specific script command.
69 
70  \par "int valid_value_count"
71  Number of valid paramters.
72 
73  \par "int (*cb)(ScriptParsing *sp, struct script_cmds *sd)"
74  This is assigned using the StringParsing Member:<br>
75  <br>
76  int assign_callback(const char *scriptCommand, int (*cb)(ScriptParsing *sp, SCRIPT_COMMANDS *sc));<br>
77  <br>
78  The function which is supplied by the programmer after the creation of the
79  Class instance is called when the command is matched. This allows the
80  programmer access to a multitude of information for further processing
81  outside of the ScriptParsing Class instance.<br>
82  <br>
83  Example:<br>
84  \verbatim
85  #include "script_parsing.h"
86 
87  static const char *modems[] = {
88  "BPSK31",
89  "MFSK32",
90  "MT63-500",
91  "MFSK64"
92  };
93 
94  int callback(ScriptParsing *sp, struct script_cmds *sc)
95  {
96  // do something
97  return 0;
98  }
99 
100  int main(int argc, const char * argv[])
101  {
102  ScriptParsing *sp = new ScriptParsing;
103 
104  if(sp) {
105  sp->assign_valid_parameters("MODEM", modems, sizeof(modems)/sizeof(char *));
106  sp->assign_callback("FILE", callback);
107  sp->parse_commands((char *)"/fldigi-dev/test_parse_commands/running_test.txt");
108  }
109 
110  return 0;
111  }
112  \endverbatim
113  <br>
114  See \ref script_parsing_class and \ref script_cmds for details about
115  what data is provided by the ScriptParsing *sp and SCRIPT_COMMANDS *sc
116  pointers. The passed SCRIPT_COMMANDS *sc pointer is a copy of the
117  original data. Modification of this information does not alter the
118  internal data.<br><br>
119  <b>Note:</b> The member and function pointers within the SCRIPT_COMMANDS
120  *sc pointer are set to dummy functions which return back to the caller
121  if executed.
122  *******************************************************************/
123 
124 #include "config.h"
125 #include "util.h"
126 
127 #include <stdio.h>
128 #include <stdlib.h>
129 #include <string.h>
130 #include <ctype.h>
131 #include <pthread.h>
132 #include <unistd.h>
133 #include <sys/stat.h>
134 
135 //#define EXTERNAL_TESTING
136 #undef EXTERNAL_TESTING
137 #ifdef EXTERNAL_TESTING
138 #define TESTING 1
139 #define LOG_INFO printf
140 #else
141 #include "debug.h"
142 #endif
143 
144 #ifdef __WIN32__
145 #define PATH_SEPERATOR "\\"
146 #define PATH_CHAR_SEPERATOR '\\'
147 #include <direct.h>
148 #define get_current_dir _getcwd
149 #else
150 #define PATH_SEPERATOR "/"
151 #define PATH_CHAR_SEPERATOR '/'
152 #include <unistd.h>
153 #define get_current_dir getcwd
154 #endif
155 
156 #include "script_parsing.h"
157 
158 // This table (by reference) is not used. Copy to another memory location.
159 // Do not change the order of this without changing the order of
160 // void ScriptParsing::initialize_function_members(void) to match.
161 
162 static const SCRIPT_COMMANDS default_script_command_table[] = {
163 	{ CMD_AUTO_LOAD_QUEUE, SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
164 	{ CMD_BASE,            SCRIPT_COMMAND | QUEUE_COMMAND, 0,  1, {0}, { p_unsigned_int },           0, 0, 0, 0 },
165 	{ CMD_BLOCKS,          SCRIPT_COMMAND,                 0,  1, {0}, { p_unsigned_int },           0, 0, 0, 0 },
166 	{ CMD_CALLFROM,        SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
167 	{ CMD_CALLTO,          SCRIPT_COMMAND | QUEUE_COMMAND, 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
168 	{ CMD_CLEAR_MISSING,   SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
169 	{ CMD_CLEAR_RXQ,       SCRIPT_COMMAND,                 0,  0, {0}, { p_null },                   0, 0, 0, 0 },
170 	{ CMD_CLEAR_TXQ,       SCRIPT_COMMAND | QUEUE_COMMAND, 0,  0, {0}, { p_null },                   0, 0, 0, 0 },
171 	{ CMD_COMP,            SCRIPT_COMMAND | QUEUE_COMMAND, 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
172 	{ CMD_EVENT_FOREVER,   SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
173 	{ CMD_EVENT_TIMED,     SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
174 	{ CMD_EVENT_TIMES,     SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
175 	{ CMD_EVENT_TYPE,      SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
176 	{ CMD_EVENT,           SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
177 	{ CMD_FILE,            SCRIPT_COMMAND | QUEUE_COMMAND, 0,  2, {0}, { p_filename, p_string },     0, 0, 0, 0 },
178 	{ CMD_HAMCAST_MODEM,   SCRIPT_COMMAND,                 0,  2, {0}, { p_unsigned_int, p_string }, 0, 0, 0, 0 },
179 	{ CMD_HAMCAST,         SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
180 	{ CMD_HDR_REPEAT,      SCRIPT_COMMAND,                 0,  1, {0}, { p_unsigned_int },           0, 0, 0, 0 },
181 	{ CMD_HEADER_MODEM,    SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
182 	{ CMD_HEADER,          SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
183 	{ CMD_INFO,            SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
184 	{ CMD_INHIBIT_HEADER,  SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
185 	{ CMD_INTERVAL,        SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
186 	{ CMD_LOAD_QUEUE,      SCRIPT_COMMAND | QUEUE_COMMAND, 0,  2, {0}, { p_string, p_string },       0, 0, 0, 0 },
187 	{ CMD_LOAD_TXDIR,      SCRIPT_COMMAND | QUEUE_COMMAND, 0,  1, {0}, { p_string, p_string },       0, 0, 0, 0 },
188 	{ CMD_MODEM,           SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
189 	{ CMD_PATH,            SCRIPT_COMMAND | QUEUE_COMMAND, 0,  1, {0}, { p_path },                   0, 0, 0, 0 },
190 	{ CMD_PROTO,           SCRIPT_COMMAND | QUEUE_COMMAND, 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
191 	{ CMD_QUEUE_FILEPATH,  SCRIPT_COMMAND | QUEUE_COMMAND, 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
192 	{ CMD_RESET,           SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
193 	{ CMD_RX_INTERVAL,     SCRIPT_COMMAND,                 0,  1, {0}, { p_unsigned_int },           0, 0, 0, 0 },
194 	{ CMD_SYNC_WITH,       SCRIPT_COMMAND,                 0,  2, {0}, { p_string, p_string },       0, 0, 0, 0 },
195 	{ CMD_TX_INTERVAL,     SCRIPT_COMMAND,                 0,  1, {0}, { p_float },                  0, 0, 0, 0 },
196 	{ CMD_TX_REPORT,       SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
197 	{ CMD_UNPROTO_MARKERS, SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
198 	{ CMD_WARN_USER,       SCRIPT_COMMAND,                 0,  1, {0}, { p_string },                 0, 0, 0, 0 },
199 	{ CMD_XMIT_REPEAT,     SCRIPT_COMMAND,                 0,  1, {0}, { p_unsigned_int },           0, 0, 0, 0 }
200 };
201 
202 /** **************************************************************
203  * \brief Assign a list of valid parameters for verification checks.
204  * \param array An Array of pointers to each element.
205  * \param array_count Number of entries in the array.
206  * \return the array count or '0' if error.
207  * \par Note:
208  * This array is limited to the first parameter of the command
209  * used in it's comparison.
210  *****************************************************************/
assign_valid_parameters(const char * command,const char ** array,const int array_count)211 int ScriptParsing::assign_valid_parameters(const char *command, const char **array, const int array_count)
212 {
213 	if(!array || array_count < 1 || !command) return 0;
214 
215 	int index = 0;
216 	int count = 0;
217 
218 	SCRIPT_COMMANDS * cmd_sc = search_command(command);
219 
220 	if(!cmd_sc) {
221 		return 0;
222 	}
223 
224 	for(index = 0; index < array_count; index++) {
225 		if(*array[index]) count++;
226 	}
227 
228 	if(count != array_count) return 0;
229 
230 	cmd_sc->valid_values = array;
231 	cmd_sc->valid_value_count = array_count;
232 
233 	return array_count;
234 }
235 
236 /** **************************************************************
237  * \brief Return true state if string is matched.
238  * \param state Referenced value to assign results to.
239  * \param string Pointer to the data string.
240  * \param true_state Pointer to the data to match with.
241  * \return SCRIPT_CODES error code.
242  *****************************************************************/
test_on_off_state(bool & state,char * string,char * true_state=(char *)"ON")243 inline SCRIPT_CODES ScriptParsing::test_on_off_state(bool &state, char *string, char *true_state=(char *)"ON")
244 {
245 	if(!string || !true_state) {
246 		return script_function_parameter_error;
247 	}
248 
249 	bool flag = false;
250 
251 	if(strncmp(string, true_state, MAX_PARAMETER_LENGTH) == 0)
252 		flag = true;
253 
254 	state = flag;
255 
256 	return script_no_errors;
257 }
258 
259 /** **************************************************************
260  * \brief Validate if file is located in the specified location.
261  * \param filename Pointer to a series of charcters
262  * \return SCRIPT_CODES error code.
263  *****************************************************************/
check_filename(char * filename,char * full_name_path=0,size_t limit=0,int path_flag=SCRIPT_COMMAND)264 SCRIPT_CODES ScriptParsing::check_filename(char *filename, char *full_name_path=0, size_t limit=0, int path_flag=SCRIPT_COMMAND)
265 {
266 	char *filename_path = (char *)0;
267 	char *path = (char *)0;
268 	SCRIPT_CODES error = script_no_errors;
269 	std::string user_path = "";
270 
271 	if(!filename) {
272 		return script_function_parameter_error;
273 	}
274 
275 	filename_path = new char[FILENAME_MAX + 1];
276 
277 	if(!filename_path) {
278 		return script_memory_allocation_error;
279 	}
280 
281 	memset(filename_path, 0, (FILENAME_MAX + 1));
282 
283 	path = new char[FILENAME_MAX + 1];
284 
285 	if(!path) {
286 		delete [] filename_path;
287 		return script_memory_allocation_error;
288 	}
289 
290 	memset(path, 0, (FILENAME_MAX + 1));
291 
292 	if(path_flag == QUEUE_COMMAND) {
293 		user_path.assign(this->queue_path());
294 	} else {
295 		user_path.assign(this->path());
296 	}
297 
298 	if(user_path.empty()) {
299 		if (!get_current_dir(path, FILENAME_MAX))
300 			strncpy(path, user_path.c_str(), FILENAME_MAX);
301 	} else {
302 		strncpy(path, user_path.c_str(), FILENAME_MAX);
303 	}
304 
305 	size_t size = strnlen(path, FILENAME_MAX);
306 
307 	if(size > 1) {
308 		if(path[size - 1] != PATH_CHAR_SEPERATOR) {
309 			strncat(path, PATH_SEPERATOR, FILENAME_MAX);
310 		}
311 	}
312 
313 	strncpy(filename_path, path, FILENAME_MAX);
314 	strncat(filename_path, filename, FILENAME_MAX);
315 
316 #ifdef TESTING
317 	printf("     filename = %s\n", filename);
318 	printf("         path = %s\n", path);
319 	printf("filename_path = %s\n", filename_path);
320 #endif
321 
322 	FILE *fd = (FILE *)0;
323 
324 	fd = fopen(filename_path, "r");
325 
326 	if(!fd) {
327 		error = script_file_not_found;
328 	} else {
329 		fclose(fd);
330 
331 		if(full_name_path && limit > 0)
332 			strncpy(full_name_path, filename_path, limit);
333 	}
334 
335 	delete [] filename_path;
336 	delete [] path;
337 
338 	return error;
339 }
340 
341 /** **************************************************************
342  * \brief Validate if path is present.
343  * \param path The path to verify.
344  * \return SCRIPT_CODES error code.
345  *****************************************************************/
check_path(const char * path)346 SCRIPT_CODES ScriptParsing::check_path(const char *path)
347 {
348 	if(!path) {
349 		return script_function_parameter_error;
350 	}
351 
352 	struct stat st;
353 	memset(&st, 0, sizeof(struct stat));
354 
355 	if(stat(path, &st) == 0) {
356 		if(st.st_mode & S_IFDIR)
357 			return script_no_errors;
358 	}
359 
360 	return script_path_not_found;
361 }
362 
363 /** **************************************************************
364  * \brief Validate if the parameter is a value.
365  * \param value The string in question.
366  * \param p format verification.
367  * \return SCRIPT_CODES error code.
368  *****************************************************************/
check_numbers(char * value,paramter_types p)369 SCRIPT_CODES ScriptParsing::check_numbers(char *value, paramter_types p)
370 {
371 	SCRIPT_CODES error = script_no_errors;
372 	size_t length = 0;
373 	size_t index = 0;
374 	int data_count = 0;
375 	int signed_value = 0;
376 	int decimal_point = 0;
377 
378 	if(!value)
379 		return script_function_parameter_error;
380 
381 	length = strnlen(value, MAX_PARAMETER_LENGTH);
382 
383 	if(length < 1)
384 		return script_parameter_error;
385 
386 	// Skip any leading white spaces.
387 	for(index = 0; index < length; index++) {
388 		if(value[index] > ' ')
389 			break;
390 	}
391 
392 	if((index >= length))
393 		return script_parameter_error;
394 
395 	switch(p) {
396 		case p_int:
397 		case p_long:
398 
399 			if(value[0] == '-' || value[0] == '+') {
400 				index++;
401 				signed_value++;
402 			}
403 
404 		case p_unsigned_int:
405 		case p_unsigned_long:
406 
407 			for(; index< length; index++) {
408 				if(isdigit(value[index]))
409 					data_count++;
410 				else
411 					break;
412 			}
413 			break;
414 
415 			if(data_count)
416 				return script_no_errors;
417 
418 		case p_float:
419 		case p_double:
420 			if(value[0] == '-' || value[0] == '+') {
421 				index++;
422 				signed_value++;
423 			}
424 
425 			for(; index< length; index++) {
426 				if(isdigit(value[index]))
427 					data_count++;
428 
429 				if(value[index] == '.')
430 					decimal_point++;
431 
432 				if(decimal_point > 1)
433 					return script_parameter_error;
434 			}
435 
436 			if(data_count)
437 				return script_no_errors;
438 
439 			break;
440 
441 		default:;
442 
443 	}
444 
445 	return error;
446 }
447 
448 /** **************************************************************
449  * \brief Validate the script parameter(s) are of the expected format.
450  * \param cmd Matching command data structure.
451  * \param p A table of expected parameters types (null terminated).
452  * \param p_count the number of 'p[]' items in the table (includes null termination).
453  * \return SCRIPT_CODES error code.
454  *****************************************************************/
check_parameters(struct script_cmds * cmd)455 SCRIPT_CODES ScriptParsing::check_parameters(struct script_cmds *cmd)
456 {
457 	SCRIPT_CODES error = script_no_errors;
458 	int count   = 0;
459 	int index   = 0;
460 	size_t size = 0;
461 
462 	if(!cmd)
463 		return script_function_parameter_error;
464 
465 	count = cmd->argc;
466 
467 	if(count < 1)
468 		return script_no_errors;
469 
470 	for(index = 0; index < count; index++) {
471 
472 		if(!cmd->args[index]) {
473 			return script_args_eol;
474 		}
475 
476 		if(cmd->param_check[index] == p_null) {
477 			size = 0;
478 		} else {
479 			size = strnlen(cmd->args[index], MAX_COMMAND_LENGTH);
480 		}
481 
482 		switch(cmd->param_check[index]) {
483 			case p_null:
484 				error = script_param_check_eol;
485 				break;
486 
487 			case p_char:
488 				if(size > 1)
489 					error = script_paramter_exceeds_length;
490 				break;
491 
492 			case p_int:
493 			case p_long:
494 			case p_unsigned_int:
495 			case p_unsigned_long:
496 			case p_float:
497 			case p_double:
498 				error = check_numbers(cmd->args[index], cmd->param_check[index]);
499 				break;
500 
501 			case p_string:
502 				if(size < 1)
503 					error = script_parameter_error;
504 				break;
505 
506 			case p_path:
507 				error = check_path(cmd->args[index]);
508 				break;
509 
510 			case p_filename:
511 				error = check_filename(cmd->args[index]);
512 				break;
513 		}
514 
515 		if(error != script_no_errors)
516 			break;
517 	}
518 
519 	return error;
520 }
521 
522 /** **************************************************************
523  * \brief Search the content of SCRIPT_COMMANDS structure table
524  * for the specified command.
525  * \param command The command to search for.
526  * \return Pointer to the matching SCRIPT_COMMANDS entry. Null if
527  * not found.
528  *****************************************************************/
search_command(const char * command)529 SCRIPT_COMMANDS * ScriptParsing::search_command(const char *command)
530 {
531 	char *cmd_buffer = (char *)0;
532 	int diff = 0;
533 	SCRIPT_COMMANDS * found = (SCRIPT_COMMANDS *) 0;
534 	size_t count = _script_command_table_count;
535 	size_t index = 0;
536 
537 	if(!command) return found;
538 
539 	cmd_buffer = new char [MAX_COMMAND_LENGTH];
540 
541 	if(!cmd_buffer) {
542 		LOG_INFO("cmd_buffer allocation error near line %d", __LINE__);
543 		return found;
544 	}
545 
546 	memset(cmd_buffer, 0, MAX_COMMAND_LENGTH);
547 	strncpy(cmd_buffer, command, MAX_COMMAND_LENGTH-1);
548 
549 	to_uppercase(cmd_buffer, (int) MAX_COMMAND_LENGTH);
550 	trim(cmd_buffer, (int) MAX_COMMAND_LENGTH);
551 
552 	for(index = 0; index < count; index++) {
553 		diff = strncmp(cmd_buffer, _script_command_table[index].command, MAX_COMMAND_LENGTH);
554 		if(diff == 0) {
555 			found = &_script_command_table[index];
556 			break;
557 		}
558 	}
559 
560 	cmd_buffer[0] = 0;
561 	delete [] cmd_buffer;
562 
563 	return found;
564 }
565 
566 /** **************************************************************
567  * \brief Convert string to uppercase characters.<br>
568  * \par str Pointer to data.
569  * \par limit data buffer size
570  * \return void
571  *****************************************************************/
to_uppercase(char * str,int limit)572 void ScriptParsing::to_uppercase(char *str, int limit)
573 {
574 	if(!str || limit < 1) return;
575 
576 	int character = 0;
577 	int count     = 0;
578 	int index     = 0;
579 
580 	count = (int) strnlen(str, limit);
581 
582 	for(index = 0; index < count; index++) {
583 		character = str[index];
584 		if(character == 0) break;
585 		character = (char) toupper(character);
586 		str[index] = character;
587 	}
588 }
589 
590 /** **************************************************************
591  * \brief Convert string to uppercase characters.<br>
592  * \par str String storage passed by reference.
593  * \return void
594  *****************************************************************/
to_uppercase(std::string & str)595 void ScriptParsing::to_uppercase(std::string &str)
596 {
597 	int character = 0;
598 	int count     = 0;
599 	int index     = 0;
600 
601 	count = (int) str.length();
602 
603 	for(index = 0; index < count; index++) {
604 		character = str[index];
605 		if(character == 0) break;
606 		character = (char) toupper(character);
607 		str[index] = character;
608 	}
609 }
610 
611 /** **************************************************************
612  * \brief Assign Call back function to a given script command.<br>
613  * \param scriptCommand Script command string<br>
614  * \param cb Pointer to call back function. int (*cb)(ScriptParsing *sp, SCRIPT_COMMANDS *sc)
615  *****************************************************************/
assign_callback(const char * scriptCommand,int (* cb)(ScriptParsing * sp,SCRIPT_COMMANDS * sc))616 int ScriptParsing::assign_callback(const char *scriptCommand, int (*cb)(ScriptParsing *sp, SCRIPT_COMMANDS *sc))
617 {
618 	char *cmd_buffer = (char *)0;
619 	int diff = 0;
620 	size_t count = _script_command_table_count;
621 	size_t index = 0;
622 
623 	if(!scriptCommand || !cb) return 0;
624 
625 	cmd_buffer = new char[MAX_COMMAND_LENGTH];
626 
627 	if(!cmd_buffer) {
628 		LOG_INFO("cmd_buffer allocation error near line %d", __LINE__);
629 		return 0;
630 	}
631 
632 	memset(cmd_buffer, 0, MAX_COMMAND_LENGTH);
633 	strncpy(cmd_buffer, scriptCommand, MAX_COMMAND_LENGTH-1);
634 
635 	to_uppercase(cmd_buffer, (int) MAX_COMMAND_LENGTH);
636 	trim(cmd_buffer, (int) MAX_COMMAND_LENGTH);
637 
638 	for(index = 0; index < count; index++) {
639 		diff = strncmp(cmd_buffer, _script_command_table[index].command, MAX_COMMAND_LENGTH);
640 		if(diff == 0) {
641 			if(_script_command_table[index].cb)
642 				LOG_INFO("Over writing call back funcion for \"%s\"", cmd_buffer);
643 			_script_command_table[index].cb = cb;
644 			break;
645 		}
646 	}
647 
648 	cmd_buffer[0] = 0;
649 	delete [] cmd_buffer;
650 
651 	return 0;
652 }
653 
654 /** **************************************************************
655  * \brief Assign func to func array checking array bounds.
656  * \param pos Position in the indexed array
657  * \param func The function (member) to assign
658  * \param limit Array count limit
659  * \return void (nothing)
660  *****************************************************************/
assign_func(size_t pos,calling_func func,size_t limit)661 void ScriptParsing::assign_func(size_t pos, calling_func func, size_t limit)
662 {
663 	if(pos < limit) {
664 		_script_command_table[pos].func = func;
665 		_script_command_table[pos].command_length = strnlen(_script_command_table[pos].command, MAX_COMMAND_LENGTH);
666 	}
667 }
668 
669 /** **************************************************************
670  * \brief Initialize callable members.
671  * \return void (nothing)
672  *****************************************************************/
defaults(bool all)673 void ScriptParsing::defaults(bool all)
674 {
675 	if(all) {
676 		_call_from        = "";
677 		_call_to          = "";
678 	}
679 
680 	_clear_missing          = false;
681 	_event                  = false;
682 	_event_forever          = false;
683 	_hamcast                = false;
684 	_hamcast_modem_1_enable = false;
685 	_hamcast_modem_2_enable = false;
686 	_hamcast_modem_3_enable = false;
687 	_hamcast_modem_4_enable = false;
688 	_inhibit_header         = false;
689 	_interval               = false;
690 	_proto                  = true;
691 	_sync_with_flamp        = false;
692 	_sync_with_fldigi       = false;
693 	_sync_with_prior        = false;
694 	_tx_report              = false;
695 	_warn_user              = false;
696 }
697 
698 /** **************************************************************
699  * \brief Initialize callable members.
700  * \return void (nothing)
701  *****************************************************************/
initialize_function_members(void)702 void ScriptParsing::initialize_function_members(void)
703 {
704 	// Ensure this is in the same sequence as the structure it's assigned to.
705 	int index = 0;
706 	assign_func(index++, &ScriptParsing::sc_auto_load_queue,     _script_command_table_total_count);
707 	assign_func(index++, &ScriptParsing::sc_base_encode,         _script_command_table_total_count);
708 	assign_func(index++, &ScriptParsing::sc_block_count,         _script_command_table_total_count);
709 	assign_func(index++, &ScriptParsing::sc_call_from,           _script_command_table_total_count);
710 	assign_func(index++, &ScriptParsing::sc_call_to,             _script_command_table_total_count);
711 	assign_func(index++, &ScriptParsing::sc_clear_missing,       _script_command_table_total_count);
712 	assign_func(index++, &ScriptParsing::sc_clear_rxq,           _script_command_table_total_count);
713 	assign_func(index++, &ScriptParsing::sc_clear_txq,           _script_command_table_total_count);
714 	assign_func(index++, &ScriptParsing::sc_compression,         _script_command_table_total_count);
715 	assign_func(index++, &ScriptParsing::sc_event_forever,       _script_command_table_total_count);
716 	assign_func(index++, &ScriptParsing::sc_event_timed,         _script_command_table_total_count);
717 	assign_func(index++, &ScriptParsing::sc_event_times,         _script_command_table_total_count);
718 	assign_func(index++, &ScriptParsing::sc_event_type,          _script_command_table_total_count);
719 	assign_func(index++, &ScriptParsing::sc_event,               _script_command_table_total_count);
720 	assign_func(index++, &ScriptParsing::sc_file,                _script_command_table_total_count);
721 	assign_func(index++, &ScriptParsing::sc_hamcast_modem,       _script_command_table_total_count);
722 	assign_func(index++, &ScriptParsing::sc_hamcast,             _script_command_table_total_count);
723 	assign_func(index++, &ScriptParsing::sc_hdr_repeat,          _script_command_table_total_count);
724 	assign_func(index++, &ScriptParsing::sc_header_modem,        _script_command_table_total_count);
725 	assign_func(index++, &ScriptParsing::sc_header,              _script_command_table_total_count);
726 	assign_func(index++, &ScriptParsing::sc_info,                _script_command_table_total_count);
727 	assign_func(index++, &ScriptParsing::sc_inhibit_header,      _script_command_table_total_count);
728 	assign_func(index++, &ScriptParsing::sc_interval,            _script_command_table_total_count);
729 	assign_func(index++, &ScriptParsing::sc_load_queue,          _script_command_table_total_count);
730 	assign_func(index++, &ScriptParsing::sc_load_txdir,          _script_command_table_total_count);
731 	assign_func(index++, &ScriptParsing::sc_modem,               _script_command_table_total_count);
732 	assign_func(index++, &ScriptParsing::sc_path,                _script_command_table_total_count);
733 	assign_func(index++, &ScriptParsing::sc_proto,               _script_command_table_total_count);
734 	assign_func(index++, &ScriptParsing::sc_queue_filepath,      _script_command_table_total_count);
735 	assign_func(index++, &ScriptParsing::sc_reset,               _script_command_table_total_count);
736 	assign_func(index++, &ScriptParsing::sc_rx_interval,         _script_command_table_total_count);
737 	assign_func(index++, &ScriptParsing::sc_sync_with,           _script_command_table_total_count);
738 	assign_func(index++, &ScriptParsing::sc_tx_interval,         _script_command_table_total_count);
739 	assign_func(index++, &ScriptParsing::sc_tx_report,           _script_command_table_total_count);
740 	assign_func(index++, &ScriptParsing::sc_unproto_makers,      _script_command_table_total_count);
741 	assign_func(index++, &ScriptParsing::sc_warn_user,           _script_command_table_total_count);
742 	assign_func(index++, &ScriptParsing::sc_xmit_repeat,         _script_command_table_total_count);
743 }
744 
745 /** **************************************************************
746  * \brief Constructor: Copy and initialize function arrays.<br>
747  *****************************************************************/
ScriptParsing()748 ScriptParsing::ScriptParsing()
749 {
750 	size_t count = 0;
751 
752 	// Initialize class variables.
753 
754 	_auto_load_queue  = false;
755 	_base             = 0;
756 	_blocks           = 0;
757 	_call_from        = "";
758 	_call_to          = "";
759 	_clear_missing    = false;
760 	_clear_rxq        = false;
761 	_clear_txq        = false;
762 	_comp             = false;
763 	_desc             = "";
764 	_envent_times     = "";
765 	_event            = false;
766 	_event_forever    = false;
767 	_event_type       = 0;
768 	_file             = "";
769 	_file_type        = SCRIPT_COMMAND;
770 	_hamcast          = false;
771 	_hamcast_modem_1  = "";
772 	_hamcast_modem_1_enable = false;
773 	_hamcast_modem_2  = "";
774 	_hamcast_modem_2_enable = false;
775 	_hamcast_modem_3  = "";
776 	_hamcast_modem_3_enable = false;
777 	_hamcast_modem_4  = "";
778 	_hamcast_modem_4_enable = false;
779 	_hdr_repeat       = 0;
780 	_info             = "";
781 	_inhibit_header   = false;
782 	_interval         = false;
783 	_modem            = "";
784 	_path             = "";
785 	_proto            = true;
786 	_queue_filename   = "";
787 	_queue_path       = "";
788 	_reset            = 0;
789 	_rx_interval      = 0;
790 	_sync_with_flamp  = false;
791 	_sync_with_fldigi = false;
792 	_sync_with_prior  = false;
793 	_tx_interval      = 0;
794 	_tx_report        = false;
795 	_warn_user        = false;
796 	_xmit_repeat      = false;
797 
798 	_sub_script_count = 0;
799 	_parent = (ScriptParsing *)0;
800 
801 	_script_command_table = (SCRIPT_COMMANDS *)0;
802 	_script_command_table_count = 0;
803 	_script_command_table_total_count = 0;
804 
805 	count = sizeof(default_script_command_table)/sizeof(SCRIPT_COMMANDS);
806 
807 	_script_command_table  = new SCRIPT_COMMANDS[count + 1];
808 
809 	if(!_script_command_table) {
810 		return;
811 	}
812 
813 	_script_command_table_count = count;
814 	_script_command_table_total_count = count + 1;
815 
816 	memset(_script_command_table, 0, sizeof(SCRIPT_COMMANDS) * _script_command_table_total_count);
817 	memcpy(_script_command_table, default_script_command_table, sizeof(default_script_command_table));
818 
819 	memset(line_buffer, 0, sizeof(line_buffer));
820 
821 	initialize_function_members();
822 
823 }
824 
825 /** **************************************************************
826  * \brief Copy environment to the sub ScriptParsing class
827  * \param src Source Class pointer to copy from.
828  *****************************************************************/
CopyScriptParsingEnv(ScriptParsing * src)829 int ScriptParsing::CopyScriptParsingEnv(ScriptParsing *src)
830 {
831 	if(!src || (src == this)) return -1;
832 
833 #if 0 // Do not copy these variables.
834 	desc(src->desc());
835 	file(src->file());
836 	load_queue(src->load_queue());
837 #endif // 0
838 
839 	auto_load_queue(src->auto_load_queue());
840 	base(src->base());
841 	blocks(src->blocks());
842 	call_from(src->call_from());
843 	call_to(src->call_to());
844 	clear_missing(src->clear_missing());
845 	clear_rxq(src->clear_rxq());
846 	clear_txq(src->clear_txq());
847 	comp(src->comp());
848 	event_forever(src->event_forever());
849 	event_times(src->event_times());
850 	event_type(src->event_type());
851 	event(src->event());
852 	hamcast_modem_1_enable(src->hamcast_modem_1_enable());
853 	hamcast_modem_1(src->hamcast_modem_1());
854 	hamcast_modem_2_enable(src->hamcast_modem_2_enable());
855 	hamcast_modem_2(src->hamcast_modem_2());
856 	hamcast_modem_3_enable(src->hamcast_modem_3_enable());
857 	hamcast_modem_3(src->hamcast_modem_3());
858 	hamcast_modem_4_enable(src->hamcast_modem_4_enable());
859 	hamcast_modem_4(src->hamcast_modem_4());
860 	hamcast(src->hamcast());
861 	hdr_repeat(src->hdr_repeat());
862 	info(src->info());
863 	inhibit_header(src->inhibit_header());
864 	interval(src->interval());
865 	modem(src->modem());
866 	path(src->path());
867 	proto(src->proto());
868 	reset(src->reset());
869 	rx_interval(src->rx_interval());
870 	sync_with_flamp(src->sync_with_flamp());
871 	sync_with_fldigi(src->sync_with_fldigi());
872 	sync_with_prior(src->sync_with_prior());
873 	tx_interval(src->tx_interval());
874 	tx_report(src->tx_report());
875 	warn_user(src->warn_user());
876 	xmit_repeat(src->xmit_repeat());
877 
878 	parent(src);
879 	script_command_table_count(src->script_command_table_count());
880 	script_command_table_total_count(src->script_command_table_total_count());
881 	sub_script_count(src->sub_script_count() + 1);
882 
883 	SCRIPT_COMMANDS * dst_table = script_command_table();
884 	SCRIPT_COMMANDS * src_table = src->script_command_table();
885 
886 	size_t index = 0;
887 	size_t count = script_command_table_count();
888 
889 	for(index = 0; index < count; index++) {
890 		dst_table[index].cb                =  src_table[index].cb;
891 		dst_table[index].valid_value_count =  src_table[index].valid_value_count;
892 		dst_table[index].valid_values      =  src_table[index].valid_values;
893 	}
894 
895 	initialize_function_members();
896 
897 	return 0;
898 }
899 
900 /** **************************************************************
901  * \brief Enable/Disable Auto load of TX queue during timed event.<br>
902  * \param cmd Pointer to matching struct script_cmds script
903  * command.
904  * \return SCRIPT_CODES error see \ref script_codes
905  * \par Script Command:<br>
906  * <tt>\"AUTO LOAD QUEUE:\<ON|OFF\>\"</tt><br>
907  * \par Script Parameters:<br>
908  * <tt>ON  = Enable</tt><br>
909  * <tt>OFF = Disable</tt><br>
910  *****************************************************************/
sc_auto_load_queue(struct script_cmds * cmd)911 SCRIPT_CODES ScriptParsing::sc_auto_load_queue(struct script_cmds *cmd)
912 {
913 	SCRIPT_CODES error = script_invalid_parameter;
914 	bool state = false;
915 
916 	if(!cmd)
917 		return script_function_parameter_error;
918 
919 	if(cmd->argc && cmd->args[0]) {
920 		error = test_on_off_state(state, cmd->args[0]);
921 
922 		if(error == script_no_errors)
923 			this->auto_load_queue(state);
924 	}
925 
926 	return error;
927 }
928 
929 /** **************************************************************
930  * \brief Set the base encoding type.<br>
931  * \param cmd Pointer to matching struct script_cmds script
932  * command.
933  * \return SCRIPT_CODES error see \ref script_codes
934  * \par Script Command:<br>
935  * <tt>\"BASE:\<64|128|256\>\"</tt><br>
936  * \par Script Parameters:<br>
937  * <tt>64 = Base64</tt><br>
938  * <tt>128 = Base128</tt><br>
939  * <tt>256 = Base256</tt><br>
940  *****************************************************************/
sc_base_encode(struct script_cmds * cmd)941 SCRIPT_CODES ScriptParsing::sc_base_encode(struct script_cmds *cmd)
942 {
943 	if(!cmd)
944 		return script_function_parameter_error;
945 
946 	int value = 0;
947 
948 	if(cmd->argc && cmd->args[0]) {
949 
950 		value = atoi(cmd->args[0]);
951 
952 		switch(value) {
953 			case  64:
954 			case 128:
955 			case 256:
956 				break;
957 
958 			default:
959 				LOG_INFO("%s Valid Parameters: 64, 128, or 256.", cmd->command);
960 				return script_invalid_parameter;
961 		}
962 
963 		this->base(value);
964 	}
965 
966 	return script_no_errors;
967 }
968 
969 /** **************************************************************
970  * \brief Set the block count.
971  * \param cmd Pointer to matching struct script_cmds script
972  * command.
973  * \return SCRIPT_CODES error see \ref script_codes
974  * \par Script Command:<br>
975  * <tt>\"Blocks:\<value\>\"</tt><br>
976  * \par Script Parameters:<br>
977  * <tt>value = 16, 32, 48, ..., 2048</tt><br>
978  *****************************************************************/
sc_block_count(struct script_cmds * cmd)979 SCRIPT_CODES ScriptParsing::sc_block_count(struct script_cmds *cmd)
980 {
981 	if(!cmd)
982 		return script_function_parameter_error;
983 
984 	int value = 0;
985 	int modulus = 0;
986 
987 	if(cmd->argc && cmd->args[0]) {
988 		value = atoi(cmd->args[0]);
989 
990 		modulus = value % 16;
991 
992 		if(modulus == 0) {
993 			this->blocks(value);
994 		}
995 		else {
996 			LOG_INFO("%s Parameters are modulus 16 values (16, 32,..., 2048).", cmd->command);
997 			return script_invalid_parameter;
998 		}
999 	}
1000 
1001 	return script_no_errors;
1002 }
1003 
1004 /** **************************************************************
1005  * \brief Set the transmitting stations callsign.
1006  * \param cmd Pointer to matching struct script_cmds script
1007  * command.
1008  * \return SCRIPT_CODES error see \ref script_codes
1009  * \par Script Command:<br>
1010  * <tt>\"CALLFROM:\<string\>\"</tt><br>
1011  * \par Script Parameters:<br>
1012  * <tt>string = ham radio operator call sign</tt><br>
1013  *****************************************************************/
sc_call_from(struct script_cmds * cmd)1014 SCRIPT_CODES ScriptParsing::sc_call_from(struct script_cmds *cmd)
1015 {
1016 	if(!cmd)
1017 		return script_function_parameter_error;
1018 
1019 	if(cmd->argc && cmd->args[0]) {
1020 		std::string value = "";
1021 
1022 		value.assign(cmd->args[0]);
1023 
1024 		if(!value.empty())
1025 			this->call_from(value);
1026 		else
1027 			return script_invalid_parameter;
1028 	}
1029 
1030 	return script_no_errors;
1031 }
1032 
1033 /** **************************************************************
1034  * \brief Set the receiving station(s) callsign.
1035  * \param cmd Pointer to matching struct script_cmds script
1036  * command.
1037  * \return SCRIPT_CODES error see \ref script_codes
1038  * \par Script Command:<br>
1039  * <tt>\"CALLTO:\<string\>\"</tt><br>
1040  * \par Script Parameters:<br>
1041  * <tt>string = ham radio operator call sign</tt>
1042  *****************************************************************/
sc_call_to(struct script_cmds * cmd)1043 SCRIPT_CODES ScriptParsing::sc_call_to(struct script_cmds *cmd)
1044 {
1045 	if(!cmd)
1046 		return script_function_parameter_error;
1047 
1048 	if(cmd->argc && cmd->args[0]) {
1049 		std::string value = "";
1050 
1051 		value.assign(cmd->args[0]);
1052 
1053 		if(!value.empty())
1054 			this->call_to(value);
1055 		else
1056 			return script_invalid_parameter;
1057 	}
1058 
1059 	return script_no_errors;
1060 }
1061 
1062 /** **************************************************************
1063  * \brief Set flag to clear received missing block on a fill
1064  * retransmission.
1065  * \param cmd Pointer to matching struct script_cmds script
1066  * command.
1067  * \return SCRIPT_CODES error see \ref script_codes
1068  * \par Script Command:<br>
1069  * <tt>\"CLEAR MISSING:\<ON|OFF\>\"</tt><br>
1070  * \par Script Parameters:<br>
1071  * <tt>ON  = Enable</tt><br>
1072  * <tt>OFF = Disable</tt><br>
1073  *****************************************************************/
sc_clear_missing(struct script_cmds * cmd)1074 SCRIPT_CODES ScriptParsing::sc_clear_missing(struct script_cmds *cmd)
1075 {
1076 	SCRIPT_CODES error = script_invalid_parameter;
1077 	bool state = false;
1078 
1079 	if(!cmd)
1080 		return script_function_parameter_error;
1081 
1082 	if(cmd->argc && cmd->args[0]) {
1083 		error = test_on_off_state(state, cmd->args[0]);
1084 
1085 		if(error == script_no_errors)
1086 			this->clear_missing(state);
1087 	}
1088 
1089 	return error;
1090 }
1091 
1092 /** **************************************************************
1093  * \brief Remove all files in the receive queue.
1094  * \param cmd Pointer to matching struct script_cmds script
1095  * command.
1096  \return SCRIPT_CODES error see \ref script_codes
1097  * \par Script Command:<br>
1098  * <tt>\"CLEAR RXQ:\"</tt><br>
1099  * \par Script Parameters:<br>
1100  * <tt>None</tt><br>
1101  *****************************************************************/
sc_clear_rxq(struct script_cmds * cmd)1102 SCRIPT_CODES ScriptParsing::sc_clear_rxq(struct script_cmds *cmd)
1103 {
1104 	return script_no_errors;
1105 }
1106 
1107 /** **************************************************************
1108  * \brief Remove all files in the transmit queue.
1109  * \param cmd Pointer to matching struct script_cmds script
1110  * command.
1111  \return SCRIPT_CODES error see \ref script_codes
1112  * \par Script Command:<br>
1113  * <tt>\"CLEAR TXQ:\"</tt><br>
1114  * \par Script Parameters:<br>
1115  * <tt>None</tt><br>
1116  *****************************************************************/
sc_clear_txq(struct script_cmds * cmd)1117 SCRIPT_CODES ScriptParsing::sc_clear_txq(struct script_cmds *cmd)
1118 {
1119 	return script_no_errors;
1120 }
1121 
1122 /** **************************************************************
1123  * \brief Enable/Disable file compression on transmitted file.
1124  * \param cmd Pointer to matching struct script_cmds script
1125  * command.
1126  \return SCRIPT_CODES error see \ref script_codes
1127  * \par Script Command:<br>
1128  * <tt>\"COMP:\<ON|OFF\>\"</tt><br>
1129  * \par Script Parameters:<br>
1130  * <tt>ON  = Enable</tt><br>
1131  * <tt>OFF = Disable</tt><br>
1132  *****************************************************************/
sc_compression(struct script_cmds * cmd)1133 SCRIPT_CODES ScriptParsing::sc_compression(struct script_cmds *cmd)
1134 {
1135 	SCRIPT_CODES error = script_invalid_parameter;
1136 	bool state = false;
1137 
1138 	if(!cmd)
1139 		return script_function_parameter_error;
1140 
1141 	if(cmd->argc && cmd->args[0]) {
1142 		error = test_on_off_state(state, cmd->args[0]);
1143 
1144 		if(error == script_no_errors)
1145 			this->comp(state);
1146 	}
1147 
1148 	return error;
1149 }
1150 
1151 /** **************************************************************
1152  * \brief Set the event type used when events are enabled.
1153  * \param cmd Pointer to matching struct script_cmds script
1154  * command.
1155  * \return SCRIPT_CODES error see \ref script_codes
1156  * \par Script Command:<br>
1157  * <tt>\"EVENT TYPE:\<string\>\"</tt><br>
1158  * \par Script Parameters:<br>
1159  * <tt>\"5 min\" = Transmit every 5 minutes</tt><br>
1160  * <tt>\"15 min\" = Transmit every 15 minutes</tt><br>
1161  * <tt>\"30 min\" = Transmit every 30 minutes</tt><br>
1162  * <tt>\"Hourly\" = Transmit every hour</tt><br>
1163  * <tt>\"Even hours\" = Transmit every even hour</tt><br>
1164  * <tt>\"Odd hours\" = Transmit every odd hour</tt><br>
1165  * <tt>\"Repeated at\" = Transmit at specific time intervals repeatedly</tt><br>
1166  * <tt>\"One time at\" = Transmit at specific time intervals once</tt><br>
1167  * <tt>\"Continuous at\" = Transmit between specific time intervals repeatedly</tt><br>
1168  *****************************************************************/
sc_event_type(struct script_cmds * cmd)1169 SCRIPT_CODES ScriptParsing::sc_event_type(struct script_cmds *cmd)
1170 {
1171 	if(!cmd)
1172 		return script_function_parameter_error;
1173 
1174 	static const char *valid_values[] = {
1175 		(char *) "5 MIN",
1176 		(char *) "15 MIN",
1177 		(char *) "30 MIN",
1178 		(char *) "HOURLY",
1179 		(char *) "EVEN HOURS",
1180 		(char *) "ODD HOURS",
1181 		(char *) "REPEATED AT",
1182 		(char *) "ONE TIME AT",
1183 		(char *) "CONTINUOUS AT"
1184 	};
1185 
1186 	static const int event_ref[] = {
1187 		et_5_min,
1188 		et_15_min,
1189 		et_30_min,
1190 		et_hourly,
1191 		et_even_hours,
1192 		et_odd_hours,
1193 		et_repeat_at,
1194 		et_one_time_at,
1195 		et_continious_at
1196 	};
1197 
1198 	int match     = -1;
1199 	SCRIPT_CODES error = script_invalid_parameter;
1200 	size_t count  = (sizeof(valid_values)/sizeof(char *));
1201 	size_t index  = 0;
1202 	std::string value = "";
1203 
1204 	if(cmd->argc && cmd->args[0]) {
1205 		value.assign(cmd->args[0]);
1206 		if(value.empty())
1207 			return script_invalid_parameter;
1208 
1209 		to_uppercase(value);
1210 
1211 		for(index = 0; index < count; index++) {
1212 			match = strncmp(value.c_str(), valid_values[index], MAX_PARAMETER_LENGTH);
1213 			if(match == 0) {
1214 				this->event_type(event_ref[index]);
1215 				return script_no_errors;
1216 			}
1217 		}
1218 	}
1219 
1220 	return error;
1221 }
1222 
1223 /** **************************************************************
1224  * \brief Assign event times
1225  * \param cmd Pointer to matching struct script_cmds script
1226  * command.
1227  * \return SCRIPT_CODES error see \ref script_codes
1228  * \par Script Command
1229  * <tt>XMIT TIMES:<start_time-end_time> (Continuous at)</tt> or
1230  * <tt>XMIT TIMES:<single_event>,<next_single_event>,... (all other event types)</tt>
1231  * All times are in 0000 through 2359 Hr (zulu/utc) format
1232  *****************************************************************/
sc_event_times(struct script_cmds * cmd)1233 SCRIPT_CODES ScriptParsing::sc_event_times(struct script_cmds *cmd)
1234 {
1235 	if(!cmd) return script_function_parameter_error;
1236 
1237 	char *cPtr   = (char *)0;
1238 	size_t count = cmd->argc;
1239 	size_t hits  = 0;
1240 	size_t index = 0;
1241 	size_t j     = 0;
1242 	size_t size  = 0;
1243 	size_t valid_data = 0;
1244 	std::string valid_string = "";
1245 
1246 	if(!count) return script_parameter_error;
1247 
1248 	valid_string.clear();
1249 
1250 	for(index = 0; index < count; index++) {
1251 		cPtr = cmd->args[index];
1252 		if(!cPtr) break;
1253 
1254 		size = strnlen(cPtr, MAX_PARAMETER_LENGTH);
1255 		hits = 0;
1256 
1257 		for(j = 0; j < size; j++) {
1258 			if(!*cPtr) break;
1259 			if(isdigit(*cPtr) || *cPtr == '-' || *cPtr == ' ') hits++;
1260 			cPtr++;
1261 		}
1262 
1263 		if(hits == size) {
1264 			valid_data++;
1265 			valid_string.append(cmd->args[index]).append(" ");
1266 		}
1267 	}
1268 
1269 	if(valid_data != count)
1270 		return script_incorrectly_assigned_value;
1271 
1272 	event_times(valid_string);
1273 
1274 	return script_no_errors;
1275 }
1276 
1277 /** **************************************************************
1278  * \brief Enable timed event to occur.
1279  * \param cmd Pointer to matching struct script_cmds script
1280  * command.
1281  * \return SCRIPT_CODES error see @ref script_codes
1282  * \par Script Command:<br>
1283  * <tt>\"EVENT TIMED:\<ON|OFF\>\"</tt><br>
1284  * \par Script Parameters:<br>
1285  * <tt>ON  = Enable</tt><br>
1286  * <tt>OFF = Disable</tt><br>
1287  *****************************************************************/
sc_event_timed(struct script_cmds * cmd)1288 SCRIPT_CODES ScriptParsing::sc_event_timed(struct script_cmds *cmd)
1289 {
1290 	SCRIPT_CODES error = script_invalid_parameter;
1291 	bool state = false;
1292 
1293 	if(!cmd)
1294 		return script_function_parameter_error;
1295 
1296 	if(cmd->argc && cmd->args[0]) {
1297 		error = test_on_off_state(state, cmd->args[0]);
1298 
1299 		if(error == script_no_errors)
1300 			this->event_timed(state);
1301 	}
1302 
1303 	return error;
1304 }
1305 
1306 /** **************************************************************
1307  * \brief Enable event transmission.
1308  * \param cmd Pointer to matching struct script_cmds script
1309  * command.
1310  * \return SCRIPT_CODES error see @ref script_codes
1311  * \par Script Command:<br>
1312  * <tt>\"EVENT:\<ON|OFF\>\"</tt><br>
1313  * \par Script Parameters:<br>
1314  * <tt>ON  = Enable</tt><br>
1315  * <tt>OFF = Disable</tt><br>
1316  *****************************************************************/
sc_event(struct script_cmds * cmd)1317 SCRIPT_CODES ScriptParsing::sc_event(struct script_cmds *cmd)
1318 {
1319 	SCRIPT_CODES error = script_invalid_parameter;
1320 	bool state = false;
1321 
1322 	if(!cmd)
1323 		return script_function_parameter_error;
1324 
1325 	if(cmd->argc && cmd->args[0]) {
1326 		error = test_on_off_state(state, cmd->args[0]);
1327 
1328 		if(error == script_no_errors)
1329 			this->event(state);
1330 	}
1331 
1332 	return error;
1333 }
1334 
1335 /** **************************************************************
1336  * \brief Enable forever timed event to occur.
1337  * \param cmd Pointer to matching struct script_cmds script
1338  * command.
1339  * \return SCRIPT_CODES error see @ref script_codes
1340  * \par Script Command:<br>
1341  * <tt>\"EVENT FOREVER:\<ON|OFF\>\"</tt><br>
1342  * \par Script Parameters:<br>
1343  * <tt>ON  = Enable</tt><br>
1344  * <tt>OFF = Disable</tt><br>
1345  * \par Note:
1346  * Requires event flag to be enabled. See ScriptParsing::sc_event()
1347  *****************************************************************/
sc_event_forever(struct script_cmds * cmd)1348 SCRIPT_CODES ScriptParsing::sc_event_forever(struct script_cmds *cmd)
1349 {
1350 	SCRIPT_CODES error = script_invalid_parameter;
1351 	bool state = false;
1352 
1353 	if(!cmd)
1354 		return script_function_parameter_error;
1355 
1356 	if(cmd->argc && cmd->args[0]) {
1357 		error = test_on_off_state(state, cmd->args[0]);
1358 
1359 		if(error == script_no_errors)
1360 			this->event_forever(state);
1361 	}
1362 
1363 	return error;
1364 }
1365 
1366 /** **************************************************************
1367  * \brief Varify and set file name / description parameters.
1368  * \param cmd Pointer to matching struct script_cmds script
1369  * command.
1370  * \return SCRIPT_CODES error see \ref script_codes
1371  * \par Script Command:<br>
1372  * <tt>\"FILE:<file_name.ext>\"</tt><br>
1373  *****************************************************************/
sc_file(struct script_cmds * cmd)1374 SCRIPT_CODES ScriptParsing::sc_file(struct script_cmds *cmd)
1375 {
1376 	if(!cmd)
1377 		return script_function_parameter_error;
1378 
1379 	char *buffer = (char *)0;
1380 	SCRIPT_CODES error = script_no_errors;
1381 	std::string value = "";
1382 
1383 	if(cmd->argc > 1) {
1384 		buffer = new char[FILENAME_MAX];
1385 
1386 		if(!buffer)
1387 			return script_memory_allocation_error;
1388 
1389 		memset(buffer, 0, FILENAME_MAX);
1390 
1391 		error = check_filename(cmd->args[0], buffer, FILENAME_MAX-1);
1392 
1393 		if(error != script_no_errors)
1394 			return error;
1395 
1396 		value.assign(buffer);
1397 
1398 		delete [] buffer;
1399 
1400 		if(value.empty())
1401 			return script_parameter_error;
1402 
1403 		this->file(value);
1404 
1405 		value.assign(cmd->args[1]);
1406 
1407 		if(value.size())
1408 			this->desc(value);
1409 		else
1410 			this->desc("");
1411 
1412 	}
1413 
1414 	return script_no_errors;
1415 }
1416 
1417 /** **************************************************************
1418  * \brief Assign modem type to hamcast position x
1419  * \param cmd Pointer to matching struct script_cmds script
1420  * command.
1421  * \return SCRIPT_CODES error see \ref script_codes
1422  * \par Script Command:<br>
1423  * <tt>\"HAMCAST MODEM:<pos>,<modem_id_string>\"</tt><br>
1424  * \par Script Parameters:<br>
1425  * <tt> pos = A value of 1, 2, 3, or 4</tt>
1426  * <tt> modem_id_string = MFSK32 (for example)</tt>
1427  *****************************************************************/
sc_hamcast_modem(struct script_cmds * cmd)1428 SCRIPT_CODES ScriptParsing::sc_hamcast_modem(struct script_cmds *cmd)
1429 {
1430 	SCRIPT_CODES error = script_no_errors;
1431 
1432 	if(!cmd)
1433 		return script_function_parameter_error;
1434 
1435 	bool flag    = false;
1436 	int diff     = 0;
1437 	int index    = 0;
1438 	int off_diff = 0;
1439 	int on_diff  = 0;
1440 	int pos      = 0;
1441 
1442 	size_t size  = 0;
1443 	std::string value = "";
1444 
1445 	if(cmd->argc > 1) {
1446 		if(cmd->args[0])
1447 			pos = atoi(cmd->args[0]);
1448 
1449 		if(pos < 1 || pos > 4) {
1450 			LOG_INFO("Parameter 1 out of range. (1, 2, 3, or 4)");
1451 			return script_invalid_parameter;
1452 		}
1453 
1454 		if(cmd->args[1]) {
1455 
1456 			value.assign(cmd->args[1]);
1457 
1458 			if(value.empty())
1459 				return script_invalid_parameter;
1460 
1461 			to_uppercase(value);
1462 
1463 			on_diff  = strncmp(value.c_str(), "ON",  MAX_PARAMETER_LENGTH);
1464 			off_diff = strncmp(value.c_str(), "OFF", MAX_PARAMETER_LENGTH);
1465 
1466 			if(on_diff == 0 || off_diff == 0) {
1467 				if(on_diff == 0) flag = true;
1468 				else flag = false;
1469 
1470 				switch(pos) {
1471 					case 1:
1472 						this->hamcast_modem_1_enable(flag);
1473 						break;
1474 					case 2:
1475 						this->hamcast_modem_2_enable(flag);
1476 						break;
1477 					case 3:
1478 						this->hamcast_modem_3_enable(flag);
1479 						break;
1480 					case 4:
1481 						this->hamcast_modem_4_enable(flag);
1482 						break;
1483 				}
1484 
1485 				return script_no_errors;
1486 			}
1487 
1488 			if(cmd->valid_values && cmd->valid_value_count) {
1489 				for(index = 0; index < cmd->valid_value_count; index++) {
1490 					diff = strncmp(cmd->args[1], cmd->valid_values[index], MAX_PARAMETER_LENGTH);
1491 					if(diff == 0) {
1492 						switch(pos) {
1493 							case 1:
1494 								this->hamcast_modem_1(value);
1495 								break;
1496 							case 2:
1497 								this->hamcast_modem_2(value);
1498 								break;
1499 							case 3:
1500 								this->hamcast_modem_3(value);
1501 								break;
1502 							case 4:
1503 								this->hamcast_modem_4(value);
1504 								break;
1505 						}
1506 
1507 						return script_no_errors;
1508 					}
1509 				}
1510 				LOG_INFO("Non-matching/unavailable modem ID string (%s).", value.c_str());
1511 				return script_invalid_parameter;
1512 			}
1513 
1514 			size = (int) strnlen(cmd->args[1], MAX_PARAMETER_LENGTH);
1515 
1516 			if(size < 4) {
1517 				error = script_invalid_parameter;
1518 			} else {
1519 				switch(pos) {
1520 					case 1:
1521 						this->hamcast_modem_1(value);
1522 						break;
1523 					case 2:
1524 						this->hamcast_modem_2(value);
1525 						break;
1526 					case 3:
1527 						this->hamcast_modem_3(value);
1528 						break;
1529 					case 4:
1530 						this->hamcast_modem_4(value);
1531 						break;
1532 				}
1533 			}
1534 		}
1535 	}
1536 
1537 	return error;
1538 }
1539 
1540 /** **************************************************************
1541  * \brief Enable hamcast events
1542  * \param cmd Pointer to matching struct script_cmds script
1543  * command.
1544  * \return SCRIPT_CODES error see \ref script_codes
1545  * \par Script Command:<br>
1546  * <tt>\"HAMCAST:\<ON|OFF\>\"</tt><br>
1547  * \par Script Parameters:<br>
1548  * <tt>ON  = Enable</tt><br>
1549  * <tt>OFF = Disable</tt><br>
1550  *****************************************************************/
sc_hamcast(struct script_cmds * cmd)1551 SCRIPT_CODES ScriptParsing::sc_hamcast(struct script_cmds *cmd)
1552 {
1553 	SCRIPT_CODES error = script_invalid_parameter;
1554 	bool state = false;
1555 
1556 	if(!cmd)
1557 		return script_function_parameter_error;
1558 
1559 	if(cmd->argc && cmd->args[0]) {
1560 		error = test_on_off_state(state, cmd->args[0]);
1561 
1562 		if(error == script_no_errors)
1563 			this->hamcast(state);
1564 	}
1565 
1566 	return error;
1567 }
1568 
1569 /** **************************************************************
1570  * \brief Enable/Disable Header modem use.<br>
1571  * \param cmd Pointer to matching struct script_cmds script
1572  * command.
1573  * \return SCRIPT_CODES error see \ref script_codes
1574  * \par Script Command:<br>
1575  * <tt>\"HEADER:\<ON|OFF\>\"</tt><br>
1576  * \par Script Parameters:<br>
1577  * <tt>ON  = Enable</tt><br>
1578  * <tt>OFF = Disable</tt><br>
1579  *****************************************************************/
sc_header(struct script_cmds * cmd)1580 SCRIPT_CODES ScriptParsing::sc_header(struct script_cmds *cmd)
1581 {
1582 	SCRIPT_CODES error = script_invalid_parameter;
1583 	bool state = false;
1584 
1585 	if(!cmd)
1586 		return script_function_parameter_error;
1587 
1588 	if(cmd->argc && cmd->args[0]) {
1589 		error = test_on_off_state(state, cmd->args[0]);
1590 
1591 		if(error == script_no_errors)
1592 			this->auto_load_queue(state);
1593 	}
1594 
1595 	return error;
1596 }
1597 
1598 /** **************************************************************
1599  * \brief Process and test modem parameters for validity.
1600  * \param cmd Pointer to matching struct script_cmds script
1601  * command.
1602  * \return SCRIPT_CODES error see \ref script_codes
1603  * \par "Script Command:"<br>
1604  * <tt>\"HEADER MODEM:\<modem_id_string\>\"<br>
1605  * Example:\"HEADER MODEM:BPSK250\"</tt><br>
1606  *****************************************************************/
sc_header_modem(struct script_cmds * cmd)1607 SCRIPT_CODES ScriptParsing::sc_header_modem(struct script_cmds *cmd)
1608 {
1609 	SCRIPT_CODES error = script_no_errors;
1610 
1611 	if(!cmd)
1612 		return script_function_parameter_error;
1613 
1614 	int diff  = 0;
1615 	int index = 0;
1616 	std::string value = "";
1617 
1618 	if(cmd->argc > 0 && cmd->args[0]) {
1619 
1620 		value.assign(cmd->args[0]);
1621 
1622 		if(value.empty())
1623 			return script_invalid_parameter;
1624 
1625 		to_uppercase(value);
1626 
1627 		if(strncmp(value.c_str(), "ON",  MAX_PARAMETER_LENGTH) == 0) {
1628 			header_modem_enable(true);
1629 			return script_no_errors;
1630 		}
1631 
1632 		if(strncmp(value.c_str(), "OFF",  MAX_PARAMETER_LENGTH) == 0) {
1633 			header_modem_enable(false);
1634 			return script_no_errors;
1635 		}
1636 
1637 		if(cmd->valid_values && cmd->valid_value_count) {
1638 			for(index = 0; index < cmd->valid_value_count; index++) {
1639 				diff = strncmp(cmd->args[0], cmd->valid_values[index], MAX_PARAMETER_LENGTH);
1640 				if(diff == 0) {
1641 					header_modem(cmd->args[0]);
1642 					return script_no_errors;
1643 				}
1644 			}
1645 			LOG_INFO("Non-matching/unavailable modem ID string (%s).", value.c_str());
1646 			return script_invalid_parameter;
1647 		}
1648 	}
1649 
1650 	return error;
1651 }
1652 
1653 /** **************************************************************
1654  * \brief Set the header repeat count
1655  * \param cmd Pointer to matching struct script_cmds script
1656  * command.
1657  * \return SCRIPT_CODES error see \ref script_codes
1658  * \par Script Command:<br>
1659  * <tt>\"HDR REPEAT:<number>\"</tt><br>
1660  * \par Script Parameters:<br>
1661  * <tt>number = 1,2,3,...,10</tt><br>
1662  *****************************************************************/
sc_hdr_repeat(struct script_cmds * cmd)1663 SCRIPT_CODES ScriptParsing::sc_hdr_repeat(struct script_cmds *cmd)
1664 {
1665 	if(!cmd)
1666 		return script_function_parameter_error;
1667 
1668 	int value = 0;
1669 
1670 	if(cmd->argc && cmd->args[0]) {
1671 		value = atoi(cmd->args[0]);
1672 
1673 		if(value < 1) value = 1;
1674 		if(value > 10) value = 10;
1675 
1676 		this->hdr_repeat(value);
1677 	}
1678 
1679 	return script_no_errors;
1680 }
1681 
1682 /** **************************************************************
1683  * \brief Assign INFO field
1684  * \param cmd Pointer to matching struct script_cmds script
1685  * command.
1686  * \return SCRIPT_CODES error see \ref script_codes
1687  * \par Script Command:<br>
1688  * <tt>\"INFO:<string>\"</tt><br>
1689  * \par Script Parameters:<br>
1690  * <tt>string = Byte value 32 to byte value 255</tt><br>
1691  *****************************************************************/
sc_info(struct script_cmds * cmd)1692 SCRIPT_CODES ScriptParsing::sc_info(struct script_cmds *cmd)
1693 {
1694 	if(!cmd)
1695 		return script_function_parameter_error;
1696 
1697 	if(cmd->argc && cmd->args[0]) {
1698 		std::string value = "";
1699 		value.assign(cmd->args[0]);
1700 
1701 		if(value.empty())
1702 			return script_incorrectly_assigned_value;
1703 
1704 		this->info(value);
1705 	}
1706 
1707 	return script_no_errors;
1708 }
1709 
1710 /** **************************************************************
1711  * \brief Enable/Disable Header modem use.
1712  * \param cmd Pointer to matching struct script_cmds script
1713  * command.
1714  * \return SCRIPT_CODES error see \ref script_codes
1715  * \par Script Command:<br>
1716  * <tt>\"INHIBIT HEADER:\<ON|OFF\>\"</tt><br>
1717  * \par Script Parameters:<br>
1718  * <tt>ON  = Enable</tt><br>
1719  * <tt>OFF = Disable</tt><br>
1720  *****************************************************************/
sc_inhibit_header(struct script_cmds * cmd)1721 SCRIPT_CODES ScriptParsing::sc_inhibit_header(struct script_cmds *cmd)
1722 {
1723 	SCRIPT_CODES error = script_invalid_parameter;
1724 	bool state = false;
1725 
1726 	if(!cmd)
1727 		return script_function_parameter_error;
1728 
1729 	if(cmd->argc && cmd->args[0]) {
1730 		error = test_on_off_state(state, cmd->args[0]);
1731 
1732 		if(error == script_no_errors)
1733 			this->inhibit_header(state);
1734 	}
1735 
1736 	return error;
1737 }
1738 
1739 /** **************************************************************
1740  * \brief Enable/Disable interval timer.
1741  * \param cmd Pointer to matching struct script_cmds script
1742  * command.
1743  * \return SCRIPT_CODES error see \ref script_codes
1744  * \par Script Command:<br>
1745  * <tt>\"INTERVAL:\<ON|OFF\>\"</tt><br>
1746  * \par Script Parameters:<br>
1747  * <tt>ON  = Enable</tt><br>
1748  * <tt>OFF = Disable</tt><br>
1749  *****************************************************************/
sc_interval(struct script_cmds * cmd)1750 SCRIPT_CODES ScriptParsing::sc_interval(struct script_cmds *cmd)
1751 {
1752 	SCRIPT_CODES error = script_invalid_parameter;
1753 	bool state = false;
1754 
1755 	if(!cmd)
1756 		return script_function_parameter_error;
1757 
1758 	if(cmd->argc && cmd->args[0]) {
1759 		error = test_on_off_state(state, cmd->args[0]);
1760 
1761 		if(error == script_no_errors)
1762 			this->interval(state);
1763 	}
1764 
1765 	return error;
1766 }
1767 
1768 /** **************************************************************
1769  * \brief Load Queue script execution.
1770  * \param cmd Pointer to matching struct script_cmds script
1771  * command.
1772  * \return SCRIPT_CODES error see \ref script_codes
1773  * \par Script Commands:
1774  * LOAD QUEUE:FILE,<file_name><br>
1775  * LOAD QUEUE:PATH,<directory_path><br>
1776  * \par Parameters
1777  * FILE = Indicates next prarmeter is a file name of the queue load script.<br>
1778  * PATH = Indicates next prarmeter is a path to the file name.<br>
1779  * \par Note(s):
1780  * This command is recursive for a maximum of MAX_SUB_SCRIPTS times.
1781  * Queue scripts use a subset of availble commands see default_script_command_table[]
1782  * int flags field for details.  The parent ScriptParsing enviroment is
1783  * duplicated to the newly created subscript. Any modification to the subscript
1784  * environment will not effect the parent.
1785  *****************************************************************/
sc_load_queue(struct script_cmds * cmd)1786 SCRIPT_CODES ScriptParsing::sc_load_queue(struct script_cmds *cmd)
1787 {
1788 	char *buffer = (char *)0;
1789 	SCRIPT_CODES error = script_no_errors;
1790 	std::string value = "";
1791 
1792 	if(sub_script_count() >= MAX_SUB_SCRIPTS)
1793 		return script_max_sub_script_reached;
1794 
1795 	if(!cmd)
1796 		return script_function_parameter_error;
1797 
1798 	if(cmd->argc > 1 && cmd->args[0] && cmd->args[1]) {
1799 		value.assign(cmd->args[0]);
1800 
1801 		if(value.empty())
1802 			return script_invalid_parameter;
1803 
1804 		to_uppercase(value);
1805 
1806 		if(strncmp(value.c_str(), "PATH", 4) == 0) {
1807 
1808 			value.assign(cmd->args[1]);
1809 			if(value.empty())
1810 				return script_invalid_parameter;
1811 
1812 			error = check_path(cmd->args[1]);
1813 
1814 			if(error != script_no_errors)
1815 				return error;
1816 
1817 			this->queue_path(value);
1818 
1819 			return script_no_errors;
1820 		}
1821 
1822 		if(strncmp(value.c_str(), "FILE", 4) == 0) {
1823 			value.assign(cmd->args[1]);
1824 			if(value.empty())
1825 				return script_invalid_parameter;
1826 
1827 			buffer = new char[FILENAME_MAX];
1828 			if(!buffer)
1829 				return script_memory_allocation_error;
1830 
1831 			memset(buffer, 0, FILENAME_MAX);
1832 
1833 			error = check_filename(cmd->args[1], buffer, FILENAME_MAX-1, QUEUE_COMMAND);
1834 
1835 			if(error != script_no_errors)
1836 				return error;
1837 
1838 			this->queue_filename(value);
1839 
1840 			ScriptParsing *sp = new ScriptParsing;
1841 
1842 			if(!sp) {
1843 				delete [] buffer;
1844 				return script_subscript_exec_fail;
1845 			}
1846 
1847 			if(sp->CopyScriptParsingEnv(this)) {
1848 				delete [] buffer;
1849 				return script_subscript_exec_fail;
1850 			}
1851 
1852 			sp->file_type(QUEUE_COMMAND);
1853 
1854 			error = sp->parse_commands(buffer);
1855 
1856 			delete [] buffer;
1857 
1858 #ifdef TESTING
1859 			printf("Modem:%s\n", this->modem().c_str());
1860 #endif // TESTING
1861 
1862 			return error;
1863 		}
1864 	}
1865 
1866 	return script_invalid_parameter;
1867 }
1868 
1869 /** **************************************************************
1870  * \brief Flag the queue loader to load from the tx directory
1871  * \param cmd Pointer to matching struct script_cmds script
1872  * command.
1873  * \return SCRIPT_CODES error see \ref script_codes
1874  * \par Script Command:<br>
1875  * <tt>\"LOAD TXDIR:\<ON|OFF\>\"</tt><br>
1876  * \par Script Parameters:<br>
1877  * <tt>ON  = Enable</tt><br>
1878  * <tt>OFF = Disable</tt><br>
1879  *****************************************************************/
sc_load_txdir(struct script_cmds * cmd)1880 SCRIPT_CODES ScriptParsing::sc_load_txdir(struct script_cmds *cmd)
1881 {
1882 	SCRIPT_CODES error = script_invalid_parameter;
1883 	bool state = false;
1884 
1885 	if(!cmd)
1886 		return script_function_parameter_error;
1887 
1888 	if(cmd->argc && cmd->args[0]) {
1889 		error = test_on_off_state(state, cmd->args[0]);
1890 
1891 		if(error == script_no_errors)
1892 			this->load_txdir(state);
1893 	}
1894 
1895 	return error;
1896 
1897 }
1898 
1899 /** **************************************************************
1900  * \brief Process and test modem parameters for validity.
1901  * \param cmd Pointer to matching struct script_cmds script
1902  * command.
1903  * \return SCRIPT_CODES error see \ref script_codes
1904  * \par "Script Command:"<br>
1905  * <tt>\"MODEM:\<modem_id_string\>\" Example:\"MODEM:BPSK250\"</tt><br>
1906  *****************************************************************/
sc_modem(struct script_cmds * cmd)1907 SCRIPT_CODES ScriptParsing::sc_modem(struct script_cmds *cmd)
1908 {
1909 	if(!cmd)
1910 		return script_function_parameter_error;
1911 
1912 	std::string value = "";
1913 
1914 	if(cmd->argc) {
1915 		value.assign(cmd->args[0]);
1916 
1917 		if(value.empty())
1918 			return script_parameter_error;
1919 
1920 		if(cmd->valid_values && cmd->valid_value_count) {
1921 			int index = 0;
1922 			int diff = 0;
1923 			for(index = 0; index < cmd->valid_value_count; index++) {
1924 				diff = strncmp(cmd->args[0], cmd->valid_values[index], MAX_PARAMETER_LENGTH);
1925 				if(diff == 0) {
1926 					this->modem(value);
1927 					return script_no_errors;
1928 				}
1929 			}
1930 			LOG_INFO("Non-matching/available modem ID string used (%s).", value.c_str());
1931 			return script_invalid_parameter;
1932 		}
1933 
1934 		this->modem(value);
1935 	}
1936 
1937 	return script_no_errors;
1938 }
1939 
1940 /** **************************************************************
1941  * \brief Base path for file access that follows.
1942  * \param cmd Pointer to matching struct script_cmds script
1943  * command.
1944  * \return SCRIPT_CODES error see \ref script_codes
1945  * \par "Script Command:"<br>
1946  * <tt>\"PATH:\<directory_path\>\" Example:\"PATH:/usr/local\"</tt><br>
1947  *****************************************************************/
sc_path(struct script_cmds * cmd)1948 SCRIPT_CODES ScriptParsing::sc_path(struct script_cmds *cmd)
1949 {
1950 	if(!cmd)
1951 		return script_function_parameter_error;
1952 
1953 	SCRIPT_CODES error = script_invalid_parameter;
1954 	std::string value = "";
1955 
1956 	if(cmd->argc) {
1957 		value.assign(cmd->args[0]);
1958 
1959 		if(value.empty())
1960 			return script_parameter_error;
1961 
1962 		error = check_path(value.c_str());
1963 
1964 		if(error == script_no_errors) {
1965 			this->path(value);
1966 		}
1967 	}
1968 
1969 	return error;
1970 }
1971 
1972 /** **************************************************************
1973  * \brief Enable/Disable AMP2 protocol in the transmitted data.
1974  * \param cmd Pointer to matching struct script_cmds script
1975  * command.
1976  * \return SCRIPT_CODES error see \ref script_codes
1977  * \par Script Command:<br>
1978  * <tt>\"PROTO:\<ON|OFF\>\"</tt><br>
1979  * \par Script Parameters:<br>
1980  * <tt>ON  = Enable</tt><br>
1981  * <tt>OFF = Disable</tt><br>
1982  *****************************************************************/
sc_proto(struct script_cmds * cmd)1983 SCRIPT_CODES ScriptParsing::sc_proto(struct script_cmds *cmd)
1984 {
1985 	SCRIPT_CODES error = script_invalid_parameter;
1986 	bool state = false;
1987 
1988 	if(!cmd)
1989 		return script_function_parameter_error;
1990 
1991 	if(cmd->argc && cmd->args[0]) {
1992 		error = test_on_off_state(state, cmd->args[0]);
1993 
1994 		if(error == script_no_errors)
1995 			this->proto(state);
1996 	}
1997 
1998 	return error;
1999 }
2000 /** **************************************************************
2001  * \brief Enable/Disable unproto makers.
2002  * \param cmd Pointer to matching struct script_cmds script
2003  * command.
2004  * \return SCRIPT_CODES error see \ref script_codes
2005  * \par Script Command:<br>
2006  * <tt>\"UNPROTO MARKERS:\<ON|OFF\>\"</tt><br>
2007  * \par Script Parameters:<br>
2008  * <tt>ON  = Enable</tt><br>
2009  * <tt>OFF = Disable</tt><br>
2010  *****************************************************************/
sc_unproto_makers(struct script_cmds * cmd)2011 SCRIPT_CODES ScriptParsing::sc_unproto_makers(struct script_cmds *cmd)
2012 {
2013 	SCRIPT_CODES error = script_invalid_parameter;
2014 	bool state = false;
2015 
2016 	if(!cmd)
2017 		return script_function_parameter_error;
2018 
2019 	if(cmd->argc && cmd->args[0]) {
2020 		error = test_on_off_state(state, cmd->args[0]);
2021 
2022 		if(error == script_no_errors)
2023 			this->unproto_markers(state);
2024 	}
2025 
2026 	return error;
2027 }
2028 
2029 
2030 /** **************************************************************
2031  * \brief Event queue loading path (full path and file name)
2032  * \param cmd Pointer to matching struct script_cmds script
2033  * command.
2034  * \return SCRIPT_CODES error see \ref script_codes
2035  * \par Script Command:<br>
2036  * <tt>\"QUEUE FILEPATH:\</path/filename.txt>\"</tt><br>
2037  *****************************************************************/
sc_queue_filepath(struct script_cmds * cmd)2038 SCRIPT_CODES ScriptParsing::sc_queue_filepath(struct script_cmds *cmd)
2039 {
2040 	if((cmd->argc > 0) && !cmd->args[0])
2041 		return script_invalid_parameter;
2042 
2043 	FILE *fd = fopen(cmd->args[0], "r");
2044 
2045 	if(fd) {
2046 		fclose(fd);
2047 		queue_filepath(cmd->args[0]);
2048 		return script_no_errors;
2049 	}
2050 
2051 	return script_file_not_found;
2052 }
2053 
2054 /** **************************************************************
2055  * \brief Reset program parameters
2056  * \param cmd Pointer to matching struct script_cmds script
2057  * command.
2058  * \return SCRIPT_CODES error see \ref script_codes
2059  * \par Script Command:<br>
2060  * <tt>\"RESET:\<ALL|PARTIAL\>\"</tt><br>
2061  * \par Script Parameters:<br>
2062  * <tt>ALL     = Complete reset of all variables</tt><br>
2063  * <tt>PARTIAL = Parital reset of variables.</tt><br>
2064  * \par NOTE:
2065  * The resting of the data is handled by the callback function.
2066  *****************************************************************/
sc_reset(struct script_cmds * cmd)2067 SCRIPT_CODES ScriptParsing::sc_reset(struct script_cmds *cmd)
2068 {
2069 	SCRIPT_CODES error = script_invalid_parameter;
2070 
2071 	if(!cmd)
2072 		return script_function_parameter_error;
2073 
2074 	if(cmd->argc && cmd->args[0]) {
2075 		int diff = 0;
2076 		std::string value;
2077 
2078 		value.assign(cmd->args[0]);
2079 		if(value.empty())
2080 			return script_invalid_parameter;
2081 
2082 		to_uppercase(value);
2083 
2084 		diff = strncmp(value.c_str(), "PARTIAL", MAX_PARAMETER_LENGTH);
2085 
2086 		if(diff == 0) {
2087 			this->reset(RESET_PARTIAL);
2088 			return script_no_errors;
2089 		}
2090 
2091 		diff = strncmp(value.c_str(), "ALL", MAX_PARAMETER_LENGTH);
2092 
2093 		if(diff == 0) {
2094 			this->reset(RESET_ALL);
2095 			return script_no_errors;
2096 		}
2097 	}
2098 
2099 	return error;
2100 }
2101 
2102 /** **************************************************************
2103  * \brief Set the receive interval time (seconds)
2104  * \param cmd Pointer to matching struct script_cmds script
2105  * command.
2106  * \return SCRIPT_CODES error see \ref script_codes
2107  * \par Script Command:<br>
2108  * <tt>\"RX INTERVAL:\<time_in_seconds\>\"</tt><br>
2109  * <tt>Value range 1-120</tt>
2110  *****************************************************************/
sc_rx_interval(struct script_cmds * cmd)2111 SCRIPT_CODES ScriptParsing::sc_rx_interval(struct script_cmds *cmd)
2112 {
2113 	if(!cmd)
2114 		return script_function_parameter_error;
2115 
2116 	int value = 0;
2117 
2118 	if(cmd->argc && cmd->args[0]) {
2119 		value = atoi(cmd->args[0]);
2120 
2121 		if(value < 1) value = 1;
2122 		if(value > 120) value = 120;
2123 
2124 		this->rx_interval(value);
2125 	}
2126 
2127 	return script_no_errors;
2128 }
2129 
2130 /** **************************************************************
2131  * \brief Modem sync method between FLAMP and FLDIGI
2132  * \param cmd Pointer to matching struct script_cmds script
2133  * command.
2134  * \return SCRIPT_CODES error see \ref script_codes
2135  * \par Script Command:<br>
2136  * <tt>\"SYNC WITH:\<mode\>,\<ON|OFF\>\"</tt><br>
2137  * \par Script Parameters:<br>
2138  * <tt>FLAMP  = FLDIGI Sync's with FLAMP</tt><br>
2139  * <tt>FLDIGI = FLAMP Sync's with FLDIGI</tt><br>
2140  * <tt>PRIOR  = Set FLDIGI to FLAMP's modem prior to transmitting data</tt>
2141  * <tt>ON     = Enable</tt>
2142  * <tt>OFF    = Disable</tt>
2143  *****************************************************************/
sc_sync_with(struct script_cmds * cmd)2144 SCRIPT_CODES ScriptParsing::sc_sync_with(struct script_cmds *cmd)
2145 {
2146 	SCRIPT_CODES error = script_invalid_parameter;
2147 	bool state = false;
2148 
2149 	char *valid_values[] = {
2150 		(char *) "FLAMP",
2151 		(char *) "FLDIGI",
2152 		(char *) "PRIOR"
2153 	};
2154 
2155 	if(!cmd)
2156 		return script_function_parameter_error;
2157 
2158 	if(cmd->argc && cmd->args[0]) {
2159 		int diff = 0;
2160 		std::string value;
2161 
2162 		value.assign(cmd->args[0]);
2163 		if(value.empty())
2164 			return script_invalid_parameter;
2165 
2166 		to_uppercase(value);
2167 
2168 		for(size_t index = 0; index < sizeof(valid_values)/sizeof(char *); index++) {
2169 			diff = strncmp(value.c_str(), valid_values[index], MAX_PARAMETER_LENGTH);
2170 
2171 			if(diff == 0) {
2172 				error = test_on_off_state(state, cmd->args[1]);
2173 
2174 				if(error == script_no_errors) {
2175 					switch(index) {
2176 						case 0:
2177 							this->sync_with_flamp(state);
2178 							break;
2179 
2180 						case 1:
2181 							this->sync_with_fldigi(state);
2182 							break;
2183 
2184 						case 2:
2185 							this->sync_with_prior(state);
2186 							break;
2187 					}
2188 				}
2189 
2190 				break;
2191 			}
2192 		}
2193 	}
2194 
2195 	return error;
2196 }
2197 
2198 /** **************************************************************
2199  * \brief Set the transmit interval in minutes
2200  * \param cmd Pointer to matching struct script_cmds script
2201  * command.
2202  * \return SCRIPT_CODES error see \ref script_codes
2203  * \par Script Command:<br>
2204  * <tt>\"TX INTERVAL:\<time_in_minutes\>\"</tt><br>
2205  * \par Script Parameters:<br>
2206  * <tt>Value range 1 to 8</tt><br>
2207  *****************************************************************/
sc_tx_interval(struct script_cmds * cmd)2208 SCRIPT_CODES ScriptParsing::sc_tx_interval(struct script_cmds *cmd)
2209 {
2210 	if(!cmd)
2211 		return script_function_parameter_error;
2212 
2213 	float value = 0;
2214 
2215 	if(cmd->argc && cmd->args[0]) {
2216 		value = atof(cmd->args[0]);
2217 
2218 		if(value < 1) value = 1;
2219 		if(value > 8) value = 8;
2220 
2221 		this->tx_interval(value);
2222 	}
2223 
2224 	return script_no_errors;
2225 }
2226 
2227 /** **************************************************************
2228  * \brief Enable/Diable transmit on report.
2229  * \param cmd Pointer to matching struct script_cmds script
2230  * command.
2231  * \return SCRIPT_CODES error see \ref script_codes
2232  * \par Script Command:<br>
2233  * <tt>\"TX REPORT:\<ON|OFF\>\"</tt><br>
2234  * \par Script Parameters:<br>
2235  * <tt>ON  = Enable</tt><br>
2236  * <tt>OFF = Disable</tt><br>
2237  *****************************************************************/
sc_tx_report(struct script_cmds * cmd)2238 SCRIPT_CODES ScriptParsing::sc_tx_report(struct script_cmds *cmd)
2239 {
2240 	SCRIPT_CODES error = script_invalid_parameter;
2241 	bool state = false;
2242 
2243 	if(!cmd)
2244 		return script_function_parameter_error;
2245 
2246 	if(cmd->argc && cmd->args[0]) {
2247 		error = test_on_off_state(state, cmd->args[0]);
2248 
2249 		if(error == script_no_errors)
2250 			this->tx_report(state);
2251 	}
2252 
2253 	return error;
2254 }
2255 
2256 /** **************************************************************
2257  * \brief
2258  * \param cmd Pointer to matching struct script_cmds script
2259  * command.
2260  * \return SCRIPT_CODES error see \ref script_codes
2261  * \par Script Command:<br>
2262  * <tt>\"WARN USER:\<ON|OFF\>\"</tt><br>
2263  * \par Script Parameters:<br>
2264  * <tt>ON  = Enable</tt><br>
2265  * <tt>OFF = Disable</tt><br>
2266  *****************************************************************/
sc_warn_user(struct script_cmds * cmd)2267 SCRIPT_CODES ScriptParsing::sc_warn_user(struct script_cmds *cmd)
2268 {
2269 	SCRIPT_CODES error = script_invalid_parameter;
2270 	bool state = false;
2271 
2272 	if(!cmd)
2273 		return script_function_parameter_error;
2274 
2275 	if(cmd->argc && cmd->args[0]) {
2276 		error = test_on_off_state(state, cmd->args[0]);
2277 
2278 		if(error == script_no_errors)
2279 			this->warn_user(state);
2280 	}
2281 
2282 	return error;
2283 }
2284 
2285 /** **************************************************************
2286  * \brief Set the number of times transmited data is repeated.
2287  * \param cmd Pointer to matching struct script_cmds script
2288  * command.
2289  * \return SCRIPT_CODES error see \ref script_codes
2290  * \par Script Command:<br>
2291  * <tt>\"XMIT REPEAT:\<count\>\"</tt><br>
2292  * \par Script Parameters:<br>
2293  * <tt>count = number of transmit repeats</tt><br>
2294  * <tt>Value range 1 - 100</tt>
2295  *****************************************************************/
sc_xmit_repeat(struct script_cmds * cmd)2296 SCRIPT_CODES ScriptParsing::sc_xmit_repeat(struct script_cmds *cmd)
2297 {
2298 	if(!cmd)
2299 		return script_function_parameter_error;
2300 
2301 	int value = 0;
2302 
2303 	if(cmd->argc && cmd->args[0]) {
2304 		value = atoi(cmd->args[0]);
2305 
2306 		if(value < 1) value = 1;
2307 		if(value > 100) value = 100;
2308 
2309 		this->xmit_repeat(value);
2310 	}
2311 
2312 	return script_no_errors;
2313 }
2314 
2315 /** **************************************************************
2316  * \brief Used for initialization of the function vector table.
2317  * \param cmd Pointer to matching struct script_cmds script
2318  * command.
2319  * \return SCRIPT_CODES error see \ref script_codes
2320  *****************************************************************/
sc_dummy(struct script_cmds * cmd)2321 SCRIPT_CODES ScriptParsing::sc_dummy(struct script_cmds *cmd)
2322 {
2323 	return script_no_errors;
2324 }
2325 
2326 /** **************************************************************
2327  * \brief Convert error numbers into human readable form.
2328  * \param error_no Error number to convert.
2329  * \param line_number The offending line number in the script file.
2330  * \param cmd The script command is question.
2331  * \return SCRIPT_CODES error see \ref script_codes
2332  *****************************************************************/
script_error_string(SCRIPT_CODES error_no,int line_number,char * cmd)2333 char * ScriptParsing::script_error_string(SCRIPT_CODES error_no, int line_number, char *cmd)
2334 {
2335 	char *es = (char *) "";
2336 
2337 	memset(error_buffer,     0, sizeof(error_buffer));
2338 	memset(error_cmd_buffer, 0, sizeof(error_cmd_buffer));
2339 	memset(error_string,     0, sizeof(error_string));
2340 
2341 	if(cmd) {
2342 		strncpy(error_cmd_buffer, cmd, sizeof(error_cmd_buffer)-1);
2343 	}
2344 
2345 	switch(error_no) {
2346 		case script_command_not_found:
2347 			es =  (char *) "Command Not Found";
2348 			break;
2349 
2350 		case script_non_script_file:
2351 			es = (char *) "Not a script file/tag not found";
2352 			break;
2353 
2354 		case script_parameter_error:
2355 			es = (char *) "Invalid parameter";
2356 			break;
2357 
2358 		case script_function_parameter_error:
2359 			es = (char *) "Invalid function parameter (internal non-script error)";
2360 			break;
2361 
2362 		case script_mismatched_quotes:
2363 			es = (char *) "Missing paired quotes (\")";
2364 			break;
2365 
2366 		case script_general_error:
2367 			es = (char *) "General Error";
2368 			break;
2369 
2370 		case script_no_errors:
2371 			es = (char *) "No Errors";
2372 			break;
2373 
2374 		case script_char_match_not_found:
2375 			es = (char *) "Character searched not found";
2376 			break;
2377 
2378 		case script_end_of_line_reached:
2379 			es = (char *) "End of line reached";
2380 			break;
2381 
2382 		case script_file_not_found:
2383 			es = (char *) "File not found";
2384 			break;
2385 
2386 		case script_path_not_found:
2387 			es = (char *) "Directory path not found";
2388 			break;
2389 
2390 		case script_args_eol:
2391 			es = (char *) "Unexpected end of parameter (args[]) list found";
2392 			break;
2393 
2394 		case script_param_check_eol:
2395 			es = (char *) "Unexpected end of parameter check list found";
2396 			break;
2397 
2398 		case script_paramter_exceeds_length:
2399 			es = (char *) "Character count in args[] parameter exceeds expectations";
2400 			break;
2401 
2402 		case script_memory_allocation_error:
2403 			es = (char *) "Memory Allocation Error (internal non-script error)";
2404 			break;
2405 
2406 		case script_incorrectly_assigned_value:
2407 			es = (char *) "Passed parameter is not of the expected type.";
2408 			break;
2409 
2410 		case script_invalid_parameter:
2411 			es = (char *) "Parameter is not valid.";
2412 			break;
2413 
2414 		case script_command_seperator_missing:
2415 			es = (char *) "Command missing ':'.";
2416 			break;
2417 
2418 		case script_max_sub_script_reached:
2419 			es = (char *) "Maximum open subscripts reached.";
2420 			break;
2421 
2422 		case script_subscript_exec_fail:
2423 			es = (char *) "Subscript execution fail (internal).";
2424 			break;
2425 
2426 		default:
2427 			es = (char *) "Undefined error";
2428 	}
2429 
2430 	snprintf(error_buffer, sizeof(error_buffer)-1, "Line: %d Error:%d %s (%s)",
2431 			 line_number, error_no, es, error_cmd_buffer);
2432 
2433 	return error_buffer;
2434 }
2435 
2436 /** **************************************************************
2437  * \brief Search for first occurrence of a non white space
2438  * \param data Data pointer to search.
2439  * \param limit Number of bytes in the data buffer.
2440  * \param error returned error code.
2441  * \return Pointer to character if found. Otherwise, return null
2442  * \par Note:<br>
2443  * The searched condition is ignored if the expected content is
2444  * encapsulated in quotes \(\"\"\).
2445  *****************************************************************/
skip_white_spaces(char * data,char * limit,SCRIPT_CODES & error)2446 char * ScriptParsing::skip_white_spaces(char * data, char * limit, SCRIPT_CODES &error)
2447 {
2448 	char *cPtr      = (char *) 0;
2449 
2450 	if(!data || !limit) {
2451 		error = script_function_parameter_error;
2452 		return (char *)0;
2453 	}
2454 
2455 	for(cPtr = data; cPtr < limit; cPtr++) {
2456 		if(*cPtr > ' ') {
2457 			error = script_no_errors;
2458 			return cPtr;
2459 		}
2460 	}
2461 
2462 	error = script_end_of_line_reached;
2463 
2464 
2465 	return (char *)0;        // End of line reached.
2466 }
2467 
2468 /** **************************************************************
2469  * \brief Search for the first occurrence on a non number.
2470  * \param data Data pointer to search.
2471  * \param limit Number of bytes in the data buffer.
2472  * \param error returned error code.
2473  * \return Pointer to character if found. Otherwise, return null
2474  * \par Note:<br>
2475  * The searched condition is ignored if the expected content is
2476  * encapsulated in quotes \(\"\"\).
2477  *****************************************************************/
skip_numbers(char * data,char * limit,SCRIPT_CODES & error)2478 char * ScriptParsing::skip_numbers(char * data, char * limit, SCRIPT_CODES &error)
2479 {
2480 	char *cPtr  = (char *) 0;
2481 	int  q_flag = 0;
2482 
2483 	if(!data || !limit) {
2484 		error = script_function_parameter_error;
2485 		return (char *)0;
2486 	}
2487 
2488 	for(cPtr = data; cPtr < limit; cPtr++) {
2489 		if(*cPtr == '"')     // Check for encapsulated strings ("")
2490 			q_flag++;
2491 
2492 		if((q_flag & 0x1))   // Continue if string is encapsulated
2493 			continue;
2494 
2495 		if(!isdigit(*cPtr)) {
2496 			error = script_no_errors;
2497 			return cPtr;
2498 		}
2499 	}
2500 
2501 	if(q_flag & 0x1) {
2502 		error = script_mismatched_quotes;
2503 	} else {
2504 		error = script_end_of_line_reached;
2505 	}
2506 
2507 	return (char *)0;        // End of line reached.
2508 }
2509 
2510 /** **************************************************************
2511  * \brief Skip characters until either a number or white space is
2512  * found.
2513  * \param data Data pointer to search.
2514  * \param limit Number of bytes in the data buffer.
2515  * \param error returned error code.
2516  * \return Pointer to character if found. Otherwise, return null
2517  * \par Note:<br>
2518  * The searched condition is ignored if the expected content is
2519  * encapsulated in quotes \(\"\"\).
2520  *****************************************************************/
skip_characters(char * data,char * limit,SCRIPT_CODES & error)2521 char * ScriptParsing::skip_characters(char * data, char * limit, SCRIPT_CODES &error)
2522 {
2523 	char *cPtr  = (char *) 0;
2524 	int  q_flag = 0;
2525 
2526 	if(!data || !limit) {
2527 		error = script_function_parameter_error;
2528 		return (char *)0;
2529 	}
2530 
2531 	for(cPtr = data; cPtr < limit; cPtr++) {
2532 		if(*cPtr == '"')     // Check for encapsulated strings ("")
2533 			q_flag++;
2534 
2535 		if((q_flag & 0x1))   // Continue if string is encapsulated
2536 			continue;
2537 
2538 		if(isdigit(*cPtr) || *cPtr <= ' ') {
2539 			error = script_no_errors;
2540 			return cPtr;
2541 		}
2542 	}
2543 
2544 	if(q_flag & 0x1) {
2545 		error = script_mismatched_quotes;
2546 	} else {
2547 		error = script_end_of_line_reached;
2548 	}
2549 
2550 	return (char *)0;        // End of line reached.
2551 }
2552 
2553 /** **************************************************************
2554  * \brief Search for the first occurrence of a white space.
2555  * \param data Data pointer to search.
2556  * \param limit Number of bytes in the data buffer.
2557  * \param error returned error code.
2558  * \return Pointer to character if found. Otherwise, return null
2559  * \par Note:<br>
2560  * The searched condition is ignored if the expected content is
2561  * encapsulated in quotes \(\"\"\).
2562  *****************************************************************/
skip_alpha_numbers(char * data,char * limit,SCRIPT_CODES & error)2563 char * ScriptParsing::skip_alpha_numbers(char * data, char * limit, SCRIPT_CODES &error)
2564 {
2565 	char *cPtr  = (char *) 0;
2566 	int  q_flag = 0;
2567 
2568 	if(!data || !limit) {
2569 		error = script_function_parameter_error;
2570 		return (char *)0;
2571 	}
2572 
2573 	for(cPtr = data; cPtr < limit; cPtr++) {
2574 
2575 		if(*cPtr == '"')     // Check for encapsulated strings ("")
2576 			q_flag++;
2577 
2578 		if((q_flag & 0x1))   // Continue if string is encapsulated
2579 			continue;
2580 
2581 		if(*cPtr <= ' ') {
2582 			error = script_no_errors;
2583 			return cPtr;
2584 		}
2585 	}
2586 
2587 	if(q_flag & 0x1) {
2588 		error = script_mismatched_quotes;
2589 	} else {
2590 		error = script_end_of_line_reached;
2591 	}
2592 
2593 	return (char *)0;        // End of line reached.
2594 }
2595 
2596 /** **************************************************************
2597  * \brief Search for first occurrence of 'character'
2598  * \param c Character to search for
2599  * \param data Pointer to Data to search for character in.
2600  * \param limit Number of bytes in the data buffer.
2601  * \param error returned error code.
2602  * \return Pointer to character if found. Otherwise, return null
2603  * \par Note:<br>
2604  * The searched condition is ignored if the expected content is
2605  * encapsulated in quotes \(\"\"\).
2606  *****************************************************************/
skip_to_character(char c,char * data,char * limit,SCRIPT_CODES & error)2607 char * ScriptParsing::skip_to_character(char c, char * data, char * limit, SCRIPT_CODES &error)
2608 {
2609 	char *cPtr  = (char *) 0;
2610 	int  q_flag = 0;
2611 
2612 	if(!data || !limit) {
2613 		error = script_function_parameter_error;
2614 		return (char *)0;
2615 	}
2616 
2617 	for(cPtr = data; cPtr < limit; cPtr++) {
2618 		if(*cPtr == '"')     // Check for encapsulated strings ("")
2619 			q_flag++;
2620 
2621 		if((q_flag & 0x1))   // Continue if string is encapsulated
2622 			continue;
2623 
2624 		if(*cPtr == c)   {    // Match found. Return pointer to it's location
2625 			error = script_no_errors;
2626 			return cPtr;
2627 		}
2628 	}
2629 
2630 	if(q_flag & 0x1) {
2631 		error = script_mismatched_quotes;
2632 	} else {
2633 		error = script_end_of_line_reached;
2634 	}
2635 
2636 	return (char *)0;        // End of line reached.
2637 }
2638 
2639 /** **************************************************************
2640  * \brief Replace CR, LF, and '#' with '0' (by value)
2641  * \param data Search data pointer
2642  * \param limit data buffer size
2643  * \return void (none)
2644  * \par Note:<br>
2645  * The searched condition is ignored if the remark character \(#\)
2646  * is encapsulated in quotes \(\"\"\).
2647  *****************************************************************/
remove_crlf_comments(char * data,char * limit,size_t & count)2648 SCRIPT_CODES ScriptParsing::remove_crlf_comments(char *data, char *limit, size_t &count)
2649 {
2650 	char *cPtr  = (char *) 0;
2651 	int  q_flag = 0;
2652 
2653 	SCRIPT_CODES error = script_no_errors;
2654 
2655 	if(!data || !limit)
2656 		return script_function_parameter_error;
2657 
2658 	count = 0;
2659 
2660 	for(cPtr = data; cPtr < limit; cPtr++) {
2661 		if(*cPtr == '\r' || *cPtr == '\n') {
2662 			*cPtr = 0;
2663 			return script_no_errors;
2664 		}
2665 
2666 		if(*cPtr == '"')
2667 			q_flag++;
2668 
2669 		if((q_flag & 0x1))
2670 			continue;
2671 
2672 		if(*cPtr == '#') {
2673 			*cPtr = 0;
2674 			break;
2675 		}
2676 
2677 		if(*cPtr > ' ')
2678 			count++;
2679 
2680 	}
2681 
2682 	// Remove trailing white spaces.
2683 	while(cPtr >= data) {
2684 		if(*cPtr <= ' ') *cPtr = 0;
2685 		else break;
2686 		cPtr--;
2687 	}
2688 
2689 	if(q_flag & 0x1) {
2690 		error = script_mismatched_quotes;
2691 	} else {
2692 		error = script_end_of_line_reached;
2693 	}
2694 
2695 	return error;
2696 }
2697 
2698 /** **************************************************************
2699  * \brief Copy memory from address to address to the source buffer.
2700  * \param buffer Destination buffer
2701  * \param sPtr Start of the copy Address
2702  * \param ePtr End of the copy Address
2703  * \param limit Destination buffer size
2704  * command.
2705  * \return SCRIPT_CODES error see \ref script_codes
2706  *****************************************************************/
copy_command(char * buffer,char * sPtr,char * ePtr,size_t limit)2707 SCRIPT_CODES ScriptParsing::copy_command(char *buffer, char *sPtr, char *ePtr, size_t limit)
2708 {
2709 	if(!buffer || !sPtr || !ePtr || limit < 1) {
2710 		return script_function_parameter_error;
2711 	}
2712 
2713 	char *dPtr   = buffer;
2714 	size_t index = 0;
2715 
2716 	for(index = 0; index < limit; index++) {
2717 		*dPtr++ = toupper(*sPtr++);
2718 		if(sPtr >= ePtr) break;
2719 	}
2720 
2721 	return script_no_errors;
2722 }
2723 
2724 /** **************************************************************
2725  * \brief Remove leading/trailing white spaces and quotes.
2726  * \param buffer Destination buffer
2727  * \param limit passed buffer size
2728  * \return void
2729  *****************************************************************/
trim(char * buffer,size_t limit)2730 void ScriptParsing::trim(char *buffer, size_t limit)
2731 {
2732 	char *s      = (char *)0;
2733 	char *e      = (char *)0;
2734 	char *dst    = (char *)0;
2735 	size_t count = 0;
2736 
2737 	if(!buffer || limit < 1) {
2738 		return;
2739 	}
2740 
2741 	for(count = 0; count < limit; count++)
2742 		if(buffer[count] == 0) break;
2743 
2744 	if(count < 1) return;
2745 
2746 	s = buffer;
2747 	e = &buffer[count-1];
2748 
2749 	for(size_t i = 0; i < count; i++) {
2750 		if((*s <= ' ') || (*s == '"')) s++;
2751 		else break;
2752 	}
2753 
2754 	while(e > s) {
2755 		if((*e <= ' ') || (*e == '"'))
2756 			*e = 0;
2757 		else
2758 			break;
2759 		e--;
2760 	}
2761 
2762 	dst = buffer;
2763 	for(; s <= e; s++) {
2764 		*dst++ = *s;
2765 	}
2766 	*dst = 0;
2767 }
2768 
2769 /** **************************************************************
2770  * \brief Parse the parameters and seperate into individual components.
2771  * \param s char pointer to the start of the string.
2772  * \param e char pointer to the end of the string.
2773  * \param matching_command pointer to the data strucure of the matching
2774  * command. See \ref SCRIPT_COMMANDS
2775  * \return SCRIPT_CODES error see \ref script_codes
2776  *****************************************************************/
parse_parameters(char * s,char * e,SCRIPT_COMMANDS * matching_command)2777 SCRIPT_CODES ScriptParsing::parse_parameters(char *s, char *e, SCRIPT_COMMANDS *matching_command)
2778 {
2779 	char *c   = s;
2780 	char *d   = (char *)0;
2781 	int index = 0;
2782 	int parameter_count = matching_command->argc;
2783 	int count = 0;
2784 	long tmp  = 0;
2785 
2786 	SCRIPT_CODES error = script_no_errors;
2787 
2788 	// Clear the old pointers.
2789 	for(index = 0; index < MAX_CMD_PARAMETERS; index++) {
2790 		matching_command->args[index] = (char *)0;
2791 	}
2792 
2793 	if(parameter_count > 0) {
2794 		count = parameter_count - 1;
2795 		for(index = 0; index < count; index++) {
2796 			c = skip_white_spaces(c, e, error);
2797 
2798 			if(error != script_no_errors)
2799 				return script_parameter_error;
2800 
2801 			d = skip_to_character(',', c, e, error);
2802 
2803 			if(error != script_no_errors)
2804 				return script_parameter_error;
2805 
2806 			*d = 0;
2807 			tmp = (long) (d - c);
2808 			if(tmp > 0)
2809 				trim(c, (size_t)(tmp));
2810 			matching_command->args[index] = c;
2811 			c = d + 1;
2812 		}
2813 
2814 		c = skip_white_spaces(c, e, error);
2815 		if(error) return error;
2816 
2817 		d = skip_alpha_numbers(c, e, error);
2818 		if(error) return error;
2819 
2820 		*d = 0;
2821 		tmp = (long) (d - c);
2822 		if(tmp > 0)
2823 			trim(c, (size_t)(tmp));
2824 
2825 		matching_command->args[index] = c;
2826 	}
2827 
2828 #ifdef TESTING
2829 	for(int i = 0; i < parameter_count;  i++)
2830 		if(matching_command->args[i])
2831 			printf("parameters %d (%s)\n", i, matching_command->args[i]);
2832 #endif
2833 
2834 	error = check_parameters(matching_command);
2835 
2836 	if(error != script_no_errors)
2837 		return error;
2838 
2839 	if(matching_command->func)
2840 		error = (this->*matching_command->func)(matching_command);
2841 	if(error) return error;
2842 
2843 
2844 	return script_no_errors;
2845 }
2846 
2847 /** **************************************************************
2848  * \brief Execute callback function.
2849  * \param cb_data Pointer for making a copy of the data to prevent
2850  * exterior alteration of source information.
2851  * \return 0 = No error<br> \< 0 = Error<br>
2852  *****************************************************************/
call_callback(SCRIPT_COMMANDS * cb_data)2853 int ScriptParsing::call_callback(SCRIPT_COMMANDS *cb_data)
2854 {
2855 	int argc     = 0;
2856 	int error    = 0;
2857 	int index    = 0;
2858 	SCRIPT_COMMANDS *tmp = (SCRIPT_COMMANDS *)0;
2859 	size_t count = 0;
2860 
2861 	if(!cb_data || !cb_data->cb) return -1;
2862 
2863 	argc = cb_data->argc;
2864 
2865 	tmp = new SCRIPT_COMMANDS;
2866 
2867 	if(!tmp) return -1;
2868 
2869 	memset(tmp, 0, sizeof(SCRIPT_COMMANDS));
2870 
2871 	for(index = 0; index < argc; index++) {
2872 		if(cb_data->args[index]) {
2873 			count = strnlen(cb_data->args[index], MAX_PARAMETER_LENGTH-1);
2874 			tmp->args[index] = new char[count+1];
2875 			if(tmp->args[index]) {
2876 				memset(tmp->args[index], 0, count+1);
2877 				strncpy(tmp->args[index], cb_data->args[index], count);
2878 			} else {
2879 				error = -1;
2880 				break;
2881 			}
2882 		} else break;
2883 	}
2884 
2885 	if(error > -1) {
2886 		// Fill SCRIPT_COMMANDS (tmp) struct with useful data.
2887 		tmp->flags          = cb_data->flags;
2888 		tmp->command_length = cb_data->command_length;
2889 		tmp->argc           = cb_data->argc;
2890 		strncpy(tmp->command, cb_data->command, MAX_COMMAND_LENGTH);
2891 
2892 		// Initialize with do nothing functions
2893 		tmp->func = &ScriptParsing::sc_dummy;
2894 		tmp->cb   = callback_dummy;
2895 
2896 		error = (*cb_data->cb)(this, tmp);
2897 	}
2898 
2899 	if(tmp) {
2900 		for(index = 0; index < argc; index++) {
2901 			if(tmp->args[index]) {
2902 				delete [] tmp->args[index];
2903 			}
2904 		}
2905 
2906 		delete tmp;
2907 	}
2908 
2909 	return error;
2910 }
2911 
2912 /** **************************************************************
2913  * \brief Parse a single line of data from the script file being read.
2914  * \param data Pointer the the script scring in question
2915  * \param buffer_size buffer size of the data pointer.
2916  * command.
2917  * \return SCRIPT_CODES error see \ref script_codes
2918  *****************************************************************/
parse_single_command(char * data,size_t buffer_size)2919 SCRIPT_CODES ScriptParsing::parse_single_command(char *data, size_t buffer_size)
2920 {
2921 	char *buffer = (char *)0;
2922 	char *cPtr   = (char *)0;
2923 	char *endPtr = (char *)0;
2924 	char *ePtr   = (char *)0;
2925 	int allocated_buffer_size = 128;
2926 	int callback_error = 0;
2927 	size_t cmd_size    = 0;
2928 	size_t cmp_results = 0;
2929 	size_t index       = 0;
2930 	size_t size        = 0;
2931 
2932 	SCRIPT_CODES error = script_no_errors;
2933 
2934 	cPtr = data;
2935 	endPtr = &data[buffer_size];
2936 
2937 	cPtr = skip_white_spaces(cPtr, endPtr, error);
2938 	if(error != script_no_errors) return error;
2939 
2940 	ePtr = skip_to_character(':', cPtr, endPtr, error);
2941 	if(error != script_no_errors) return script_command_seperator_missing;
2942 
2943 	buffer = new char [allocated_buffer_size];
2944 	if(!buffer) {
2945 		LOG_INFO("Buffer allocation Error near File: %s Line %d", __FILE__, __LINE__);
2946 		return script_memory_allocation_error;
2947 	}
2948 
2949 	memset(buffer, 0, allocated_buffer_size);
2950 	error = copy_command(buffer, cPtr, ePtr, allocated_buffer_size-1);
2951 	if(error != script_no_errors) {
2952 		buffer[0] = 0;
2953 		delete [] buffer;
2954 		return error;
2955 	}
2956 
2957 	int str_count = str_cnt(buffer, allocated_buffer_size);
2958 	trim(buffer, str_count);
2959 
2960 	for(index = 0; index < _script_command_table_count; index++) {
2961 		size = strnlen(_script_command_table[index].command, MAX_COMMAND_LENGTH);
2962 		cmd_size = strnlen(buffer, MAX_COMMAND_LENGTH);
2963 		cmp_results = memcmp(buffer, _script_command_table[index].command, size);
2964 
2965 		if(cmp_results == 0 && (cmd_size == size)) {
2966 			if(file_type() & _script_command_table[index].flags) {
2967 				error = parse_parameters(++ePtr, endPtr, &_script_command_table[index]);
2968 				if(error)  {
2969 					buffer[0] = 0;
2970 					delete [] buffer;
2971 					return error;
2972 				}
2973 
2974 				if(_script_command_table[index].cb) {
2975 					callback_error = call_callback(&_script_command_table[index]);
2976 					if(callback_error < 0)
2977 						LOG_INFO("Call back for script command %s reported an Error", _script_command_table[index].command);
2978 				}
2979 			} else {
2980 				LOG_INFO("Command %s ignored, not supported in current file type:", buffer);
2981 			}
2982 			break;
2983 		}
2984 	}
2985 
2986 	buffer[0] = 0;
2987 	delete [] buffer;
2988 
2989 	return script_no_errors;
2990 }
2991 
2992 /** **************************************************************
2993  * \brief Script entry point for parsing the script file.
2994  * \param file_name_path path and file name for the script to parse.
2995  * \return SCRIPT_CODES error see \ref script_codes
2996  *****************************************************************/
parse_commands(char * file_name_path)2997 SCRIPT_CODES ScriptParsing::parse_commands(char *file_name_path)
2998 {
2999 	bool log_error  = false;
3000 	char *cPtr      = (char *)0;
3001 	FILE *fd        = (FILE *)0;
3002 	int line_number = 0;
3003 	SCRIPT_CODES error_code = script_no_errors;
3004 	size_t count    = 0;
3005 	size_t tmp      = 0;
3006 
3007 	if(!file_name_path) {
3008 		LOG_INFO("Invalid function parameter 'char *file_name_path' (null)");
3009 		return script_general_error;
3010 	}
3011 
3012 	fd = fopen(file_name_path, "r");
3013 
3014 	if(!fd) {
3015 		LOG_INFO("Unable to open file %s", file_name_path);
3016 		return script_general_error;
3017 	}
3018 
3019 	memset(line_buffer, 0, sizeof(line_buffer));
3020 
3021 	line_number++;
3022 	char *retval = fgets(line_buffer, sizeof(line_buffer) - 1, fd);
3023 
3024 	tmp = strlen(SCRIPT_FILE_TAG);
3025 	line_buffer[tmp] = 0;
3026 	tmp = strncmp(SCRIPT_FILE_TAG, line_buffer, tmp);
3027 
3028 	if(!retval || tmp) {
3029 		cPtr = script_error_string(script_non_script_file, line_number, line_buffer);
3030 		LOG_INFO("%s", cPtr);
3031 		fclose(fd);
3032 		return script_non_script_file;
3033 	}
3034 
3035 	while(1) {
3036 		if(ferror(fd) || feof(fd)) break;
3037 
3038 		memset(line_buffer, 0, sizeof(line_buffer));
3039 		if(fgets(line_buffer, sizeof(line_buffer) - 1, fd))
3040 			line_number++;
3041 
3042 #ifdef TESTING
3043 		printf("Reading: %s", line_buffer);
3044 #endif
3045 
3046 		error_code = remove_crlf_comments(line_buffer, &line_buffer[sizeof(line_buffer)], count);
3047 
3048 		if(count < 1) {
3049 			continue;
3050 		}
3051 
3052 #ifdef TESTING
3053 		printf("remove_crlf_comments(%s)\n", line_buffer);
3054 #endif
3055 
3056 		if(error_code >= script_no_errors)
3057 			error_code = parse_single_command(line_buffer, sizeof(line_buffer) - 1);
3058 
3059 		if(error_code != script_no_errors) {
3060 			LOG_INFO("%s", script_error_string(error_code, line_number, line_buffer));
3061 			log_error = true;
3062 		}
3063 	}
3064 
3065 	fclose(fd);
3066 
3067 	if(log_error) {
3068 		return script_general_error;
3069 	}
3070 
3071 	return script_no_errors;
3072 }
3073 
3074 /** **************************************************************
3075  * \brief Destructors
3076  *****************************************************************/
~ScriptParsing()3077 ScriptParsing::~ScriptParsing()
3078 {
3079 	if(_script_command_table)
3080 		delete [] _script_command_table;
3081 }
3082 
3083 /** **************************************************************
3084  * \brief Dummy callback function for initialization of
3085  * function pointers.
3086  * \param sp The calling ScriptParsing Class
3087  * \param sc Command data structure pointer to the matching script
3088  * command.
3089  * \return 0 = No error<br> \< 0 = Error<br>
3090  *****************************************************************/
callback_dummy(ScriptParsing * sp,struct script_cmds * sc)3091 int callback_dummy(ScriptParsing *sp, struct script_cmds *sc)
3092 {
3093 	return 0;
3094 }
3095 
3096 /** ********************************************************
3097  * \brief Determine the length of the string with a count
3098  * limitation.
3099  * \return signed integer. The number of characters in the
3100  * array not to excede count limit.
3101  ***********************************************************/
str_cnt(char * str,int count_limit)3102 int ScriptParsing::str_cnt(char * str, int count_limit)
3103 {
3104 	if(!str || (count_limit < 1))
3105 		return 0;
3106 
3107 	int value = 0;
3108 
3109 	for(int index = 0; index < count_limit; index++) {
3110 		if(str[index] == 0) break;
3111 		value++;
3112 	}
3113 
3114 	return value;
3115 }
3116 
3117 
3118 
3119 
3120