/* START LIBRARY DESCRIPTION *********************************************
PARSER.LIB
	Copyright (c) 1997, Avtech Electrosystems Ltd.

DESCRIPTION:
	Parser functions.

SUPPORT LIB'S:
END DESCRIPTION **********************************************************/


#include "parser.h"
#include <glib/gprintf.h>

//STATICS
static int query_min_max_float (gchar** response, char *parameter, float min_val, float max_val);
static int query_float (gchar** response, float value);
static int process_float_param (char *parameter, float *value, float min_val, float max_val, int zero_mode);
static int check_channel_ok (int channel, int enabled_channels, char chankey);
static int Parser_id_word(char *id_me, int *channel, int *with_id_code);
static int Parser_find_commands(int commands[], int command_depth);
static int Parser_get_unit(char *parameter,char *units);
static int Parser_channel (int *channel,int with_id_code,int routine_num);
static int Go_freq_32_33(gchar** response, int channel, char *parameter,char *units,int command_type);
static int Handle_Units(float *mult,char *units, char *base);
static int Is_Min_Command(char *text);
static int Is_Max_Command(char *text);
static int Go_syst_err_11(gchar** response, int channel, char *parameter,char *units,int command_type);
static int Go_syst_errcnt66(gchar** response, int channel, char *parameter,char *units,int command_type);

static int Parser_id_word(char *id_me, int *channel, int *with_id_code)
{
	int id_code;
	int set_new_channel;

	set_new_channel=0;

	/* detect channel suffixes in the range of 0-99 */
	if (isdigit((int) id_me[strlen(id_me)-1])) {
		set_new_channel=1;
		*channel=atoi(id_me+strlen(id_me)-1);
		if (isdigit((int) id_me[strlen(id_me)-2])) {
			*channel=atoi(id_me+strlen(id_me)-2);
			id_me[strlen(id_me)-2]=0;
		} else {
			id_me[strlen(id_me)-1]=0;
		}
	}


	if (!strcmp(id_me,"*cls") ) {
		id_code = 1;
	} else if (!strcmp(id_me,"*ese") ) {
		id_code = 2;
	} else if (!strcmp(id_me,"*esr") ) {
		id_code = 4;
	} else if (!strcmp(id_me,"*idn") ) {
		id_code = 5;
	} else if (!strcmp(id_me,"*opc") ) {
		id_code = 6;
	} else if (!strcmp(id_me,"*rst") ) {
		id_code = 8;
	} else if (!strcmp(id_me,"*sre") ) {
		id_code = 9;
	} else if (!strcmp(id_me,"*stb") ) {
		id_code = 10;
	} else if (!strcmp(id_me,"*tst") ) {
		id_code = 11;
	} else if (!strcmp(id_me,"*wai") ) {
		id_code = 12;
	} else if (!strcmp(id_me,"system")		 || !strcmp(id_me,"syst")) {
		id_code = 13;
	} else if (!strcmp(id_me,"error")		  || !strcmp(id_me,"err"))	{
		id_code = 14;
	} else if (!strcmp(id_me,"version")		|| !strcmp(id_me,"vers")) {
		id_code = 15;
	} else if (!strcmp(id_me,"status")		 || !strcmp(id_me,"stat")	|| !strcmp(id_me,"state")) {
		id_code = 16;
	} else if (!strcmp(id_me,"operation")	 || !strcmp(id_me,"oper")) {
		id_code = 17;
	} else if (!strcmp(id_me,"event")		  || !strcmp(id_me,"even")) {
		id_code = 18;
	} else if (!strcmp(id_me,"condition")	 || !strcmp(id_me,"cond")) {
		id_code = 19;
	} else if (!strcmp(id_me,"enable")		 || !strcmp(id_me,"enab")) {
		id_code = 20;
	} else if (!strcmp(id_me,"questionable") || !strcmp(id_me,"ques")) {
		id_code = 21;
	} else if (!strcmp(id_me,"preset")		 || !strcmp(id_me,"pres")) {
		id_code = 22;
	} else if (!strcmp(id_me,"diagnostic")	|| !strcmp(id_me,"diag")) {
		id_code = 23;
	} else if (!strcmp(id_me,"output")		 || !strcmp(id_me,"outp")) {
		id_code = 24;
	} else if (!strcmp(id_me,"impedance")	 || !strcmp(id_me,"imp"))	{
		id_code = 25;
	} else if (!strcmp(id_me,"protection")	|| !strcmp(id_me,"prot")) {
		id_code = 26;
	} else if (!strcmp(id_me,"tripped")		|| !strcmp(id_me,"trip")) {
		id_code = 27;
	} else if (!strcmp(id_me,"source")		 || !strcmp(id_me,"sour")) {
		id_code = 28;
	} else if (!strcmp(id_me,"current")		|| !strcmp(id_me,"curr")) {
		id_code = 29;
	} else if (!strcmp(id_me,"level")		  || !strcmp(id_me,"lev"))	{
		id_code = 30;
	} else if (!strcmp(id_me,"immediate")	 || !strcmp(id_me,"imm"))	{
		id_code = 31;
	} else if (!strcmp(id_me,"amplitude")	 || !strcmp(id_me,"ampl")) {
		id_code = 32;
	} else if (!strcmp(id_me,"offset")		 || !strcmp(id_me,"offs")) {
		id_code = 33;
	} else if (!strcmp(id_me,"high") ) {
		id_code = 34;
	} else if (!strcmp(id_me,"low") ) {
		id_code = 35;
	} else if (!strcmp(id_me,"voltage")		|| !strcmp(id_me,"volt")) {
		id_code = 36;
	} else if (!strcmp(id_me,"frequency")	 || !strcmp(id_me,"freq")) {
		id_code = 37;
	} else if (!strcmp(id_me,"function")	  || !strcmp(id_me,"func")) {
		id_code = 38;
	} else if (!strcmp(id_me,"shape")		  || !strcmp(id_me,"shap")) {
		id_code = 39;
	} else if (!strcmp(id_me,"pulse")		  || !strcmp(id_me,"puls")) {
		id_code = 40;
	} else if (!strcmp(id_me,"period")		 || !strcmp(id_me,"per"))	{
		id_code = 41;
	} else if (!strcmp(id_me,"width")		  || !strcmp(id_me,"widt")) {
		id_code = 42;
	} else if (!strcmp(id_me,"dcycle")		 || !strcmp(id_me,"dcyc")) {
		id_code = 43;
	} else if (!strcmp(id_me,"hold") ) {
		id_code = 44;
	} else if (!strcmp(id_me,"delay")		  || !strcmp(id_me,"del"))	{
		id_code = 45;
	} else if (!strcmp(id_me,"double")		 || !strcmp(id_me,"doub")) {
		id_code = 46;
	} else if (!strcmp(id_me,"polarity")	  || !strcmp(id_me,"pol"))	{
		id_code = 47;
	} else if (!strcmp(id_me,"beeper")		 || !strcmp(id_me,"beep")) {
		id_code = 48;
	} else if (!strcmp(id_me,"cw") )	{
		id_code = 49;
	} else if (!strcmp(id_me,"fixed")		  || !strcmp(id_me,"fix"))	{
		id_code = 50;
	} else if (!strcmp(id_me,"abort")		  || !strcmp(id_me,"abor")) {
		id_code = 51;
	} else if (!strcmp(id_me,"initiate")	  || !strcmp(id_me,"init")) {
		id_code = 52;
	} else if (!strcmp(id_me,"continuous")) {
		id_code = 53;
	} else if (!strcmp(id_me,"trigger")		|| !strcmp(id_me,"trig")) {
		id_code = 54;
	} else if (!strcmp(id_me,"integer")		|| !strcmp(id_me,"int"))	{
		id_code = 55;
	} else if (!strcmp(id_me,"float") )	{
		id_code = 56;
	} else if (!strcmp(id_me,"eprom") ) {
		id_code = 57;
	} else if (!strcmp(id_me,"string") ) {
		id_code = 58;
	} else if (!strcmp(id_me,"bit") ) {
		id_code = 59;
	} else if (!strcmp(id_me,"set") ) {
		id_code = 60;
	} else if (!strcmp(id_me,"clear") )	{
		id_code = 61;
	} else if (!strcmp(id_me,"shiftreg") )	{
		id_code = 62;
	} else if (!strcmp(id_me,"*rcl") ) {
		id_code = 63;
	} else if (!strcmp(id_me,"*sav") ) {
		id_code = 64;
	} else if (!strcmp(id_me,"gate") ) {
		id_code = 65;
	} else if (!strcmp(id_me,"local")  || !strcmp(id_me,"logout") || !strcmp(id_me,"exit") || !strcmp(id_me,"quit") )	{
		id_code = 66;
	} else if (!strcmp(id_me,"test") ) {
		id_code = 67;
	} else if (!strcmp(id_me,"communicate")  || !strcmp(id_me,"comm")) {
		id_code = 68;
	} else if (!strcmp(id_me,"address")  		|| !strcmp(id_me,"addr")) {
		id_code = 69;
	} else if (!strcmp(id_me,"gpib") ) {
		id_code = 70;
	} else if (!strcmp(id_me,"serial")  		|| !strcmp(id_me,"ser")) {
		id_code = 71;
	} else if (!strcmp(id_me,"receive")  		|| !strcmp(id_me,"rec")) {
		id_code = 72;
	} else if (!strcmp(id_me,"baud") ) {
		id_code = 73;
	} else if (!strcmp(id_me,"parity")  		|| !strcmp(id_me,"par")) {
		id_code = 74;
	} else if (!strcmp(id_me,"type") ) {
		id_code = 75;
	} else if (!strcmp(id_me,"bits") ) {
		id_code = 76;
	} else if (!strcmp(id_me,"sbits")  		|| !strcmp(id_me,"sbit")) {
		id_code = 77;
	} else if (!strcmp(id_me,"echo") ) {
		id_code = 78;
	} else if (!strcmp(id_me,"rts") ) {
		id_code = 79;
	} else if (!strcmp(id_me,"control")		|| !strcmp(id_me,"cont"))	{
		id_code = 80;
	} else if (!strcmp(id_me,"next") ) {
		id_code = 81;
	} else if (!strcmp(id_me,"count")			|| !strcmp(id_me,"coun"))	{
		id_code = 82;
	} else if (!strcmp(id_me,"display")		|| !strcmp(id_me,"disp"))	{
		id_code = 83;
	} else if (!strcmp(id_me,"brightness")	|| !strcmp(id_me,"brig"))	{
		id_code = 84;
	} else if (!strcmp(id_me,"load") ) {
		id_code = 85;
	} else if (!strcmp(id_me,"measure")	  	|| !strcmp(id_me,"meas"))	{
		id_code = 86;
	} else if (!strcmp(id_me,"char") ) {
		id_code = 87;
	} else if (!strcmp(id_me,"calibration") 	|| !strcmp(id_me,"cal"))	{
		id_code = 88;
	} else if (!strcmp(id_me,"monitor")	  	|| !strcmp(id_me,"mon"))	{
		id_code = 89;
	} else if (!strcmp(id_me,"step") ) {
		id_code = 90;
	} else if (!strcmp(id_me,"route")		  || !strcmp(id_me,"rout")) {
		id_code = 91;
	} else if (!strcmp(id_me,"close")		  || !strcmp(id_me,"clos")) {
		id_code = 92;
	} else if (!strcmp(id_me,"point") ) {
		id_code = 93;
	} else if (!strcmp(id_me,"shift") ) {
		id_code = 94;
	} else if (!strcmp(id_me,"scale") ) {
		id_code = 95;
	} else if (!strcmp(id_me,"null") ) {
		id_code = 96;
	} else if (!strcmp(id_me,"size") ) {
		id_code = 97;
	} else if (!strcmp(id_me,"separation") 	|| !strcmp(id_me,"sep"))	{
		id_code = 98;
	} else if (!strcmp(id_me,"network") 		|| !strcmp(id_me,"net"))	{
		id_code = 99;
	} else if (!strcmp(id_me,"password") 	|| !strcmp(id_me,"pass"))	{
		id_code = 100;
	} else if (!strcmp(id_me,"new") ) {
		id_code = 101;
	} else if (!strcmp(id_me,"suspend") ) {
		id_code = 102;
	} else if (!strcmp(id_me,"transition") 	|| !strcmp(id_me,"tran"))	{
		id_code = 103;
	} else if (!strcmp(id_me,"leading") 	|| !strcmp(id_me,"lead"))	{
		id_code = 104;
	} else if (!strcmp(id_me,"limit") 	|| !strcmp(id_me,"lim"))	{
		id_code = 105;
	} else if (!strcmp(id_me,"slew") )	{
		id_code = 106;
	} else if (!strcmp(id_me,"all") ) {
		id_code = 107;
	}

	else {
		id_code = 9999;
	}

	if (set_new_channel) {
		*with_id_code=id_code;
	}

	return id_code;

}

static int Parser_find_commands(int commands[], int command_depth)
{
#define optional  16384 /* set bit high if optional */
#define opt_mask 16383  /* default bit mask */
#define max_tokens_in_sentence 7
#define max_no_of_sentences 110

	/* Valid command -sequences-. The -individual- commands are defined in id_word. */

	static const int sentences[max_no_of_sentences][max_tokens_in_sentence] =	{
		{0},	/* null - error */
		{1},	/* cls - sentence 1 */
		{2},	/* ese - sentence 2 */
		{4},	/* esr - sentence 3 */
		{5},	/* idn - sentence 4 */
		{6},	/* opc - sentence 5 */
		{8},	/* rst - sentence 6 */
		{9},	/* sre - sentence 7 */
		{10},  /* stb - sentence 8 */
		{11},  /* tst - sentence 9 */
		{12},  /* wai - sentence 10 */
		{13,14,81|optional},	  /* syst:err:next - 11 */
		{13,15},					  /* syst:vers - 12 */
		{16,17,18|optional},	  /* stat:oper:event - 13 */
		{16,17,19},				  /* stat:oper:cond - 14 */
		{16,17,20},				  /* stat:oper:enable - 15 */
		{16,21,18|optional},	  /* stat:ques:event - 16 */
		{16,21,19},				  /* stat:ques:cond - 17 */
		{16,21,20},				  /* stat:ques:enable - 18 */
		{16,22},					  /* stat:preset - 19 */
		{24,25},					  /* output:impedance - 20 */
		{24,26,27},				  /* output:prot:tripped - 21 */
		{28|optional,29,30|optional,31|optional,32|optional},  /* sour:curr:lev:imm:ampl - 22 */
		{28|optional,29,30|optional,31|optional,33},			  /* sour:curr:lev:imm:offset - 23 - not used */
		{28|optional,29,30|optional,31|optional,34},			  /* sour:curr:lev:imm:high - 24 - not used */
		{28|optional,29,30|optional,31|optional,35},			  /* sour:curr:lev:imm:low - 25 */
		{28|optional,36,30|optional,31|optional,32|optional},  /* sour:volt:lev:imm:ampl - 26 */
		{28|optional,36,30|optional,31|optional,33},			  /* sour:volt:lev:imm:offset - 27 - not used */
		{28|optional,36,30|optional,31|optional,34},			  /* sour:volt:lev:imm:high - 28 - not used */
		{28|optional,36,30|optional,31|optional,35},			  /* sour:volt:lev:imm:low - 29 */
		{28|optional,29,26,27},			  /* sour:curr:prot:tripped? - 30 */
		{28|optional,36,26,27},			  /* sour:volt:prot:tripped? - 31 */
		{28|optional,37,49|optional},	  /* sour:freq:cw - 32 */
		{28|optional,37,50|optional},	  /* sour:freq:fixed - 33 */
		{28|optional,38,39|optional},	  /* sour:func:shape - 34 */
		{28|optional,40,41},				  /* sour:puls:per - 35 */
		{28|optional,40,42},				  /* sour:puls:width - 36 */
		{28|optional,40,43},				  /* sour:puls:dcyc - 37 */
		{28|optional,40,44},				  /* sour:puls:hold - 38 */
		{28|optional,40,45},				  /* sour:puls:delay - 39 */
		{28|optional,40,46,16|optional},  /* sour:puls:doub:state - 40 */
		{28|optional,40,46,45},			  /* sour:puls:doub:delay - 41 */
		{28|optional,40,47},				  /* sour:puls:pol - 42 */
		{13,48,31|optional},	  /* syst:beep:imm - 43 - not used */
		{52,31|optional,53},	  /* init:imm:cont - 44 - not used */
		{51},						  /* abort - 45 - not used */
		{54,31|optional,28},	  /* trig:imm:sour - 46 */
		{23,57,58},				  /* diag:eprom:string - 47 */
		{23,57,55},				  /* diag:eprom:int - 48 */
		{23,57,59,60},			  /* diag:eprom:bit:set - 49 - not used */
		{23,57,59,61},			  /* diag:eprom:bit:clear - 50 - not used */
		{23,57,56},				  /* diag:eprom:float - 51 */
		{23,62},					  /* diag:shiftreg - 52 */
		{63},						  /* *rcl - 53 */
		{64},						  /* *sav - 54 */
		{24,16|optional},			/* output:state - 55 */
		{28|optional,40,65,75},		/* sour:puls:gate:type - 56 */
		{66},							/* local (RS232 mode only) - 57 */
		{23,67,45},					/* diag:test:delay - 58 */
		{13,68,70,69},				/* syst:comm:gpib:addr - 59 */
		{13,68,71,72|optional,73},	/* syst:comm:ser:rec:baud - 60 */
		{13,68,71,72|optional,74,75|optional},	/* syst:comm:ser:rec:parity - 61 */
		{13,68,71,72|optional,76},	/* syst:comm:ser:rec:bits - 62 */
		{13,68,71,72|optional,77},	/* syst:comm:ser:rec:sbits - 63 */
		{13,68,71,80,79},			/* syst:comm:ser:control:rts - 64 */
		{13,68,71,72|optional,78},	/* syst:comm:ser:rec:echo - 65 */
		{13,14,82},	  			/* syst:err:count - 66 */
		{28|optional,40,65,30},		/* sour:puls:gate:level - 67 */
		{24,85},					  /* output:load - 68 */
		{86,32},					  /* meas:ampl? - 69 */
		{23,57,87},				  /* diag:eprom:char - 70 */
		{23,88},				  	/* diag:calib - 71 */
		{23,32,88,95|optional}, 	/* diag:ampl:calib:scale - 72 */
		{23,89,88,95|optional}, 	/* diag:mon:calib:scale - 73 */
		{23,89,90},				 	/* diag:mon:step - 74 */
		{24,75},					 	/* output:type - 75 */
		{23,33,88,95|optional}, 	/* diag:offset:calib:scale - 76 */
		{23,40,42,88,94},		/* diag:pulse:width:calib:shift - 77 */
		{91,92},						/* route:close - 78 */
		{23,40,42,88,93},			/* diag:pulse:width:calib:point - 79 */
		{23,40,45,88,93},			/* diag:pulse:delay:calib:point - 80 */
		{23,40,41,88,93},			/* diag:pulse:period:calib:point - 81 */
		{23,40,45,88,94},			/* diag:pulse:delay:calib:shift - 82 */
		{23,32,88,93},				/* diag:ampl:calib:point - 83 */
		{23,33,88,93},				/* diag:offset:calib:point - 84 */
		{23,33,96,93},				/* diag:offset:null:point - 85 */
		{23,57,97},				  /* diag:eprom:size - 86 */
		{83,84},				  	/* display:brightness - 87 */
		{28|optional,40,82},	  /* sour:puls:count - 88 */
		{28|optional,40,98},	  /* sour:puls:separation - 89 */
		{23,40,98,88,93},			/* diag:pulse:separation:calib:point - 90 */
		{13,68,99},					/* system:comm:network - 91 */
		{13,100,101},				/* system:password:new - 92 */
		{23,57,102},				  /* diag:eprom:suspend - 93 */
		{28|optional,40,103,104|optional},  /* sour:puls:transition:leading - 94 */
		{23,40,103,88,93},			/* diag:pulse:transition:calib:point - 95 */
		{28|optional,29,105,32|optional},	/* sour:curr:limit:ampl - 96 */
		{23,24,85,88,93},			/* diag:output:load:calib:point - 97 */
		{28|optional,29,106},  		/* sour:curr:slew - 98 */
		{23,106,88,93},				/* diag:slew:calib:point - 99 */
		{88,107|optional},  			/* calibration:all - 100 */
		{88,37}			  			/* calibration:frequency - 101 */

	};


	int token_num;		 /* which token in the command[] list is being considered */
	int sentence_num;	 /* which sentence is being considered as a match */
	int sentence_found;  /* matching sentence found yet? */
	int sent_pos;		  /* position in current sentence */
	int invalid_match;	/* flag if definitely the wrong match */
	int local_match_pos; /* position (or pos+1, depends) of most-recently-matched token in sentence */
	int local_match;	  /* match for current token found yet? */
	int prev_match;		/* were there previous local matches? */
	int i;					/* a counter */

	sentence_num = 1;
	sent_pos = 0;

	/* check each "sentence" (corresponding to a different routine or function)
		until a match is found, or until their are no possible sentences left */
	for (sentence_found = 0; (!sentence_found && sentence_num < max_no_of_sentences); ++sentence_num) {
		local_match_pos=0;
		invalid_match=0;
		prev_match=0;
		/* start with the first input token, until all have been matched, or one
		is unmatchable */
		for (token_num = 0; (token_num < command_depth) && !invalid_match; ++token_num) {
			/* start after most recent matching token */
			sent_pos=local_match_pos;

			/* compare current token to the sentence components until a matching component
				is found, or until there are none left */
			for (local_match=0; (!local_match) && (sent_pos<max_tokens_in_sentence); ++sent_pos) {

				/* compare here */
				if (commands[token_num]==((sentences[sentence_num][sent_pos]) & opt_mask)) {
					/* flag a local token match (not a whole sentence match) */
					local_match=1;
				}
			}
			if (local_match) {
				/* if any of the components in the sentence were skipped between the last two
					matches, make sure that they were optional ones */
				if (!prev_match) {
					local_match_pos=-1;
				}
				/* if there was no local match previously, then the initial value
					of local_match_pos is misleading - there was no match at 0. */
				for (i=(local_match_pos+1); i<(sent_pos-1); ++i) {
					/* if they weren't optional, the match is not valid */
					if (!(sentences[sentence_num][i] & optional)) {
						invalid_match=1;
					}
				}
				/* counteract the last "++i" due to the for loop */
				local_match_pos = sent_pos - 1;
				if (local_match && !invalid_match) {
					prev_match=1;
				}
			}

			/* if no match for this one token existed in this sentence, mark this sentence as invalid */
			if (!local_match) {
				invalid_match = 1;
			}
		}
		/* if no invalid token match was found for any of the tokens in this sentence,
			then it's an overall sentence match! */
		if (!invalid_match) {
			sentence_found=1;
		}
	}
	--sentence_num;

	if (!sentence_found) {
		sentence_num = 0;
	}
	/* 0 is the no-match value */

	return sentence_num;
}


static int Parser_get_unit(char *parameter,char *units)
{
	/* this function takes a parameter like "1e+6 ms" and breaks it into parameter="1e+6" and
		units="ms" */

	int i;
	int j;
	int units_present;

	units_present=0;
	i=0;


	if (isdigit(parameter[0]) || parameter[0]=='+' || parameter[0] == '-' || parameter[0] == '.') {
		for (i=1; (i < strlen(parameter)) && isdigit(parameter[i]); ++i) {}

		if (i < strlen(parameter))
			if ( parameter[i]=='.' )
				for (++i; (i < strlen(parameter)) && isdigit(parameter[i]); ++i) {}

		/* suck out spaces */
		while ( (i<strlen(parameter)) && (isspace(parameter[i])) ) {
			for (j=i; j<strlen(parameter); ++j) {
				parameter[j]=parameter[j+1];
			}
			parameter[j]=0;
		}

		if (i < strlen(parameter)) {
			if (	  (parameter[i]=='e' && parameter[i+1]=='-')
			                || (parameter[i]=='e' && parameter[i+1]=='+') ) {
				for (i+=2; (i < strlen(parameter)) && isdigit(parameter[i]); ++i) {}
			}
			else if (parameter[i]=='e' && isdigit(parameter[i+1]) ) {
				for (i+=2; (i < strlen(parameter)) && isdigit(parameter[i]); ++i) {}
			}
		}

		/* suck out spaces */
		while ( (i<strlen(parameter)) && (isspace(parameter[i])) ) {
			for (j=i; j<strlen(parameter); ++j) {
				parameter[j]=parameter[j+1];
			}
			parameter[j]=0;
		}

		if (i != strlen(parameter)) {
			units_present=1;
			strcpy(units,parameter+i);
			parameter[i]=0;

			/* remove trailing spaces */
			for (j=strlen(units)-1; isspace(units[j]); --j) {
				units[j]=0;
			}

		}
	}

	return units_present;
}

void Parser_main (char *in, int interactive_terminal, void(*cbfunc)(gpointer, gchar *), gpointer user_data)
{
	int in_pos;				 /* this identifies the single character of in being processed */
	int command_depth;		/* how many command words have been parsed */
	int old_command_depth;	/* save this if in case of a compound message */
	char current_command[max_input_word_length]; /* current command word being processed */
	int current_command_length;						/* length of current command so far */
	int commands[max_commands_in_input];			/* list of identified command tokens */
	int old_commands[max_commands_in_input];	  /* list of identified command tokens from */
	/* last non-common, non-colon-started command*/
	int space_found;		  /* if true, a space was found. parameter may follow */
	int parameter_found;	 /* if true, there is a parameter at the end of the command string */
	char 	parameter[max_input_word_length];		 /* identified parameter */
	char 	units[max_input_word_length];			  /* identified units */
	int semicolon_found;								 /* to break up lines */
	int units_found;		  /* text found after parameter must be units */
	int i;													/* counter */
	int routine_num;		  	/* routine to execute, based on command */
	int dont_use_this_header;	/* the old head is saved in case of compound message, unless this is set */
	int compound_message;		/* this indicates that at least one semicolon has been found, so */
	/* the message is a compound one */


	int is_query;
	int command_type;			/* combination of is_query, parameter_found, and units_found */

	int channel;
	int with_id_code;

	// fixme - delete enclosing loop and re-indent
	if (1) {
		strcpy(in+strlen(in)," "); /* add white space to the end of the input string */

		in_pos = 0;							 /* start at the beginning of the input string */
		semicolon_found = 0;
		compound_message = 0;
		command_depth=0;
		old_command_depth=0;
		dont_use_this_header=NO;

		while (isspace(in[in_pos]) || in[in_pos]==':' || in[in_pos]==';') {
			/* ignore leading spaces */
			++in_pos;
		}


		/* examine each letter in the string until EOS */
		while (in[in_pos] != 0) {

			channel=-1;
			with_id_code=0;
			space_found = 0;
			parameter_found = 0;							 /* no numeric parameters yet */
			parameter[0] = 0;
			units_found = 0;
			is_query = 0;
			current_command_length = 0;

			if (semicolon_found && !dont_use_this_header) {
				old_command_depth=command_depth-2;
				for (i=0; i<old_command_depth; ++i) {
					old_commands[i]=commands[i];
				}
			}

			command_depth=old_command_depth;
			for (i=0; i<command_depth; ++i) {
				commands[i]=old_commands[i];
			}

			semicolon_found=0;
			dont_use_this_header=NO;

			/* break it up if semicolons are present */
			while (!semicolon_found && in[in_pos] != 0) {

				in[in_pos]=tolower(in[in_pos]);
				/* everything to lowercase */

				/* words are separated by white space or colons or semicolons */
				if ( isspace(in[in_pos]) || in[in_pos] == ':' || in[in_pos] == ';')

				{

					if (in[in_pos] == ';') {
						++semicolon_found;
					}
					++compound_message;

					/* if it is a separator, ignore it if it is duplicated */
					if ( !isspace(in[in_pos-1])  && (in[in_pos-1] != ':') && (in[in_pos-1] != ';')) {
						/* valid separator, so terminate current command word string */
						current_command[current_command_length] = '\0';

						if (space_found) {
							/* Just end things if it is a semicolon */
							if (in[in_pos]!=';') {
								current_command[current_command_length] = in[in_pos];
								++current_command_length;
							}

							current_command[current_command_length] = '\0';
							++parameter_found;
							strcpy(parameter,current_command);
						} else if (!space_found) {
							/* just a regular command word */

							/* terminate the current command string */
							current_command_length = 0;	/* make room for a new command string */
							commands[command_depth]=Parser_id_word(current_command,&channel,&with_id_code);

							if (	  commands[command_depth]<13
							                || commands[command_depth]==63
							                || commands[command_depth]==64) {
								dont_use_this_header=YES;
								commands[0]=commands[command_depth];
								command_depth=0;
							}

							++command_depth;		  /* indicate that a new command word is ready */

							/* indicate if the separator was a space - next bit parameter then */
							if (in[in_pos]==' ') {
								++space_found;
							}
						}
					} /* non-repeated separator */
					else if (compound_message && in[in_pos-1]==';' && in[in_pos]==':') {
						dont_use_this_header=YES;
						command_depth=0;
					}

				} /* a separator */
				else if (in[in_pos]!='?') {
					/* if not white space or separator, add character to current command word */
					current_command[current_command_length] = in[in_pos];
					++current_command_length;
				} /* not a separator */
				else {
					++is_query;
					/* question mark found */
				}


				++in_pos;							/* move pointer to next letter */

			} /* not a semi-colon */

			commands[command_depth]=0;
			++command_depth;
			/* add end of tokens marker (0) */

			int error_num=OK;
			gchar *response = NULL;
			gchar *error_response = NULL;

			if (parameter_found) {
				units_found = Parser_get_unit(parameter,units);
			}

			if (!error_num) {
				if (!units_found) {
					units[0]='\0';
				}
				g_strstrip (parameter);
				command_type=(is_query<<2) | (parameter_found?2:0) | units_found;

				if (commands[0]!=0) {
					routine_num = Parser_find_commands(commands,command_depth);

					/* check for valid channel number, based on position in command */
					if ( (error_num=Parser_channel(&channel,with_id_code,routine_num)) )
						/* not a valid command due to improper channel suffix */
					{
						routine_num=9999;
					}
				} else {
					routine_num=9999;
					/* ignore empty commands lists, generated by a white space string */
				}

				switch (routine_num) {
				case 0:
					response = g_strdup_printf("routine_num: %d, channel: %d, parameter: %s, units: %s, command type: %d",routine_num,channel,parameter,units,command_type);
					error_num=Unrecognized;
					break;
				case 11:
					error_num=Go_syst_err_11(&response,channel,parameter,units,command_type);
					break;
				case 32:
				case 33:
					error_num=Go_freq_32_33(&response,channel,parameter,units,command_type);
					break;
				case 66:
					error_num=Go_syst_errcnt66(&response,channel,parameter,units,command_type);
					break;

				case 9999:
					// was only whitespace, ignore
					break;

				default:
					/* valid but not implemented yet */
					response = g_strdup_printf("routine_num: %d, channel: %d, parameter: %s, units: %s, command type: %d",routine_num,channel,parameter,units,command_type);
					break;

				}
			}

 			if (error_num) {
				queue_error_from_parser(&error_response, error_num);
				if (interactive_terminal) {
					(*cbfunc)(user_data, error_response);
				}
			} else {
				(*cbfunc)(user_data, response);
			}

			g_free (response);
                        g_free (error_response);

			if (!is_query) {
				Main_update_shift_registers();    /* update values in pulse generator circuit */
			}

		} /* not EOS */

		/* update display if it wasn't a query, and if the control menu isn't on (this gives the user
		a chance to press "Go To Local" to override control */

		int Selected_Submenu=0, Submenu1_rem_loc=0, Type_Of_Menu=0, Submenu_On=0;

		if (!is_query && !(Selected_Submenu==Submenu1_rem_loc && Type_Of_Menu==Submenu_On)) {
			Menu_Update_Display();
		}

		/* re-run error_check to update min/max values based on actual settings, not proposed settings */
		Error_check(globals.ChannelState);
	}

	// trigger a prompt
	if (interactive_terminal) {
		(*cbfunc)(user_data, "");
	}
}

static int Parser_channel (int *channel,int with_id_code,int routine_num)
{
	/* return if no channel suffixes appeared */
	if (*channel==-1) {
		*channel=0;
		return OK;
	}


	/* compare to maximum number of channels allowed */
	if (*channel>1 && *channel>globals.Flash.channels && !globals.Flash.enable_avrq_extra_ampls) {
		return InvalidChannel;
	}
	if (*channel>5 && globals.Flash.enable_avrq_extra_ampls) {
		return InvalidChannel;
	}


	/* SCPI uses 1-N counting, change to 0 - (N-1) */
	if (*channel > 0) {
		--(*channel);
	} else {
		return InvalidChannel;
	}

	return OK;
}


static int Go_freq_32_33(gchar** response, int channel, char *parameter,char *units,int command_type)
{
	// this is a typical parser function - there will be many more added later

	float new_freq;
	int status;

	if ( (status = check_channel_ok (channel, globals.Flash.channels, globals.Flash.ChanKey_frequency))) {
		return status;
	}

	new_freq=1.0;

	switch (command_type) {
	case command_param_units:
		if ( (status=Handle_Units(&new_freq,units,"hz")) ) {
			return status;
		}
		/* no break */

	case command_withparam:
		if ( (status = process_float_param (parameter, &new_freq, 1.0, 1.0e6, NORMAL_ZERO)) ) {
			return status;
		}
		return Set_frequency(0,0,0,channel,new_freq);
		break;

	case query_simple:
		return query_float(response, globals.ChannelState[channel].frequency);
		break;

	case query_param:
		return query_min_max_float (response, parameter, 1.0, 1.0e6);
		break;

	default:
		return SyntaxError;
		break;
	}
}


static int Handle_Units(float *mult,char *units, char *in_base)
{
	int len_base, len_all, pos;
	char prefix[max_input_word_length];
	char base[max_input_word_length];

	strcpy (base, in_base);

	*mult=1.0;
	if (!strcmp(units,base)) {
		return OK;
	}

	// match base
	len_all = strlen (units);
	if (len_all == 0) {
		return OK;
	}

	len_base = strlen (base);
	pos = len_all - len_base;

	strcpy (prefix, units);

	if (!strcmp(base,"%") && (len_all >= 3) && !strcmp(prefix + len_all - 3,"pct")) {
		strcpy(base, "pct");
		prefix[len_all - 3] = 0;
	} else if (!strcmp(base,"pct") && !strcmp(prefix + len_all - 1,"%")) {
		strcpy(base, "%");
		prefix[len_all - 1] = 0;
	} else if (strcmp(prefix+pos,base)) {
		return UnknownUnits;
	} else {
		prefix[pos]=0;
	}

	if (strlen(prefix) == 0) {
		return OK;
	}

	// special exceptions
	if (!strcmp(prefix,"m") && !strcmp(base,"ohm")) {
		*mult=1.0e6;
	} else if (!strcmp(prefix,"m") && !strcmp(base,"hz")) {
		*mult=1.0e6;
	}
	// normal rules
	else if (!strcmp(prefix,"ex")) {
		*mult=1.0e18;
	} else if (!strcmp(prefix,"pe")) {
		*mult=1.0e15;
	} else if (!strcmp(prefix,"t")) {
		*mult=1.0e12;
	} else if (!strcmp(prefix,"g")) {
		*mult=1.0e9;
	} else if (!strcmp(prefix,"ma")) {
		*mult=1.0e6;
	} else if (!strcmp(prefix,"k")) {
		*mult=1.0e3;
	} else if (!strcmp(prefix,"m")) {
		*mult=1.0e-3;
	} else if (!strcmp(prefix,"u")) {
		*mult=1.0e-6;
	} else if (!strcmp(prefix,"n")) {
		*mult=1.0e-9;
	} else if (!strcmp(prefix,"p")) {
		*mult=1.0e-12;
	} else if (!strcmp(prefix,"f")) {
		*mult=1.0e-15;
	} else if (!strcmp(prefix,"a")) {
		*mult=1.0e-18;
	} else {
		return UnknownUnits;
	}

	return OK;
}


static int Is_Min_Command(gchar *text)
{
	return (!strcmp(text,"min") || !strcmp(text,"minimum"));
}


static int Is_Max_Command(char *text)
{
	return (!strcmp(text,"max") || !strcmp(text,"maximum"));
}


static int query_min_max_float (gchar** response, char *parameter, float min_val, float max_val)
{
	float report_val;

	if (Is_Min_Command(parameter)) {
		report_val=min_val;
	} else if (Is_Max_Command(parameter)) {
		report_val=max_val;
	} else {
		return SyntaxError;
	}

	Float_To_Text(remote_digits_after_decimal,report_val,response);
	return OK;
}

static int query_float (gchar** response, float value)
{
	Float_To_Text(remote_digits_after_decimal,value,response);
	return OK;
}

static int process_float_param (char *parameter, float *value, float min_val, float max_val, int zero_mode)
{
	int status;
	status = OK;

	if (String_is_it_numeric(parameter)) {
		*value *= atof(parameter);

		if (zero_mode == ALLOW_NEG_ZERO) {
			/* allow a value of "-0" */
			if (fabs(*value) < smallest_allowed_number)
				if (parameter[0] == '-') {
					*value = -0.1*smallest_allowed_number;
				}
		}
	}

	else if (!strcmp(parameter,"+") && (zero_mode == ALLOW_NEG_ZERO)) {
		*value = smallest_allowed_number;
	} else if (!strcmp(parameter,"-") && (zero_mode == ALLOW_NEG_ZERO)) {
		*value = -smallest_allowed_number;
	} else if (Is_Min_Command(parameter)) {
		*value = min_val;
	} else if (Is_Max_Command(parameter)) {
		*value = max_val;
	} else {
		status=SyntaxError;
	}

	return status;
}

static int check_channel_ok (int channel, int enabled_channels, char chankey)
{
	/* how many channels overall */
	if (channel >= enabled_channels) {
		return InvalidChannel;
	}

	/* abandon if high channel selected by user but not enabled by firmware for that parameter*/
	if (channel && !chankey) {
		return InvalidChannel;
	}

	return OK;
}


static int Go_syst_err_11(gchar** response, int channel, char *parameter,char *units,int command_type)
{
	switch (command_type) {
	case query_simple:
		if (globals.number_of_errors==0) {
			get_error_text(response, OK);
		} else {
			get_error_text(response,globals.error_queue[1]);
			Error_Remove_From_Queue();
		}
		return OK;
		break;

	default:
		return SyntaxError;
		break;
	}
}

static int Go_syst_errcnt66(gchar** response, int channel, char *parameter,char *units,int command_type)
{
	switch (command_type) {
	case query_simple:
		return query_int (response, globals.number_of_errors);
		break;

	default:
		return SyntaxError;
		break;
	}
}