/*
 * This is part of an example of a client application for EasyBTX.
 *
 * Written by: Max Boehm, 01/29/95
 *
 * You may freely copy, distribute and reuse the code in this example.  
 * Don't even talk to me about warranties.
 */


#import <libc.h>
#import <ctype.h>

#import "parse_rule.h"

#define INI 0x13	/* ini       (*)     */
#define TER 0x1C	/* ter       (#)     */
#define APH 0x1E	/* home      (*021#) */
#define APU 0x0B	/* up        (*022#) */
#define APB 0x08	/* back      (*024#) */
#define APF 0x09	/* forward   (*026#) */
#define APR 0x0D	/* return    (*027#) */
#define APD 0x0A	/* down      (*028#) */
#define DCT 0x1A	/* send page (*029#) */


static const char *rule;	/* global pointer into the current rule */


void set_rule (const char *rule_string)
/* Sets the rule to parse. rule_string is not copied and must not be changed
 * until processing of the rule has finished.
 */
{
    rule=rule_string;
}


void skip_space (void)
{
    while (isspace(*rule))		/* skip white space */
	rule++;
}


BOOL expect_char (char c)
/* Skips white space characters. Reads the next character, if it is c.
 * Returns YES if c was read, otherwise NO.
 */
{
    skip_space();
    if (*rule==c) {
	rule++;
	return YES;
    }
    return NO;
}


BOOL expect_string (const char *expect)
/* Skips white space characters. If the next strlen(expect) characters are
 * equal to the string expect[], they are read.
 * Returns YES if expect[] was read, otherwise NO.
 */
{
    skip_space();
    if (strncmp(rule,expect,strlen(expect))==0) {
	rule+=strlen(expect);
	return YES;
    }
    return NO;
}


BOOL get_state (char *state, int max_state_len)
/* Skips white space characters. Reads maximal max_state_len-1 characters
 * (letters, digits, '_') and writes them to state[] followed by '\0'.
 * Returns YES on success, otherwise NO.
 */
{
    char *help=state;
    
    skip_space();
    while (--max_state_len>0 && (isalnum(*rule)||*rule=='_'))
	*state++=*rule++;
    *state=0;
    return state>help;
}


BOOL get_int (int *value, int min, int max)
/* Reads an integer which have to be within the rage min..max.
 * Returns YES if successful, otherwise NO
 */
{
    const char *help=rule;
    
    *value=strtol(rule,&rule,10);		/* value */
    if (*value>=min && *value<=max)
	return YES;
    rule=help;
    return NO;
}


BOOL get_row_col (int *row, int *col)
/* Reads a position of the form row,col with row=1..24 and col=1..40.
 * Returns YES on success. Returns NO if row or col is outside the bounds,
 * or if the syntax is wrong.
 */
{
    const char *help=rule;
    
    if (get_int(row,1,24))			/* row */
	if (expect_char(','))
	    if (get_int(col,1,40))		/* col */
		return YES;
    rule=help;
    return NO;
}


BOOL get_string (char *str, int max_str_len, const char *input_var[])
/* A string of the form "string" is read (terminated by double quotes).
 * At most max_str_len characters are written to str[] including the '\0'
 * terminator. '\' is used as escape character:
 * \"=", \\=\, \0..\9=input_var[0]..input_var[9], \n=newline,
 * \*=INI, \#=TER, \!=DCT,
 * \h=home, \b=back, \f=forward, \u=up, \d=down, \r=return
 * Returns YES on success, otherwise NO.
 */
{
    const char *help=rule;
    
    if (expect_char('"')) {
	max_str_len--;
	while (max_str_len>0 && *rule && *rule!='"') {
	    char c=*rule++;
	    if (c=='\\') {				/* escape code */
		if (*rule==0)
		    break;
		c=*rule++;
		if (c>='0' && c<='9') {			/* \0..\9 */
		    const char *s=input_var[c-'0'];
		    while (*s && max_str_len>0) {
			*str++=*s++;
			max_str_len--;
		    }
		    continue;
		}
		switch (c) {
		    case '*': c=INI; break;
		    case '#': c=TER; break;
		    case '!': c=DCT; break;
		    case 'h': c=APH; break;
		    case 'b': c=APB; break;
		    case 'f': c=APF; break;
		    case 'u': c=APU; break;
		    case 'd': c=APD; break;
		    case 'r': c=APR; break;
		    case 'n': c='\n'; break;
		}
	    }
	    *str++=c;
	    max_str_len--;
	}
	*str=0;
	if (expect_char('"'))
	    return YES;
    }
    rule=help;
    return NO;
}
