/*
Copyright 1990-2001 Sun Microsystems, Inc. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions: The above copyright notice and this
permission notice shall be included in all copies or substantial
portions of the Software.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.


Except as contained in this notice, the names of The Open Group and/or
Sun Microsystems, Inc. shall not be used in advertising or otherwise to
promote the sale, use or other dealings in this Software without prior
written authorization from The Open Group and/or Sun Microsystems,
Inc., as applicable.


X Window System is a trademark of The Open Group

OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
logo, LBX, X Window System, and Xinerama are trademarks of the Open
Group. All other trademarks and registered trademarks mentioned herein
are the property of their respective owners. No right, title or
interest in or to any trademark, service mark, logo or trade name of
Sun Microsystems, Inc. or its licensors is granted.

*/
/******************************************************************

              Copyright 1992 by Oki Technosystems Laboratory, Inc.
              Copyright 1992 by Fuji Xerox Co., Ltd.

Permission to use, copy, modify, distribute, and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and
that both that copyright notice and this permission notice appear
in supporting documentation, and that the name of Oki Technosystems
Laboratory and Fuji Xerox not be used in advertising or publicity
pertaining to distribution of the software without specific, written
prior permission.
Oki Technosystems Laboratory and Fuji Xerox make no representations
about the suitability of this software for any purpose.  It is provided
"as is" without express or implied warranty.

OKI TECHNOSYSTEMS LABORATORY AND FUJI XEROX DISCLAIM ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL OKI TECHNOSYSTEMS
LABORATORY AND FUJI XEROX BE LIABLE FOR ANY SPECIAL, INDIRECT OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
OR PERFORMANCE OF THIS SOFTWARE.

  Author: Yasuhiro Kawai	Oki Technosystems Laboratory
  Author: Kazunori Nishihara	Fuji Xerox

******************************************************************/


#ifndef lint
#ifdef sccs
static char     sccsid[] = "@(#)XimpParser.c	2.8 95/09/12 SMI";
#endif
#endif

#include <stdio.h>
#ifndef __XLIBINT_HH
#define __XLIBINT_HH
#include <X11/Xlibint.h>
#endif /* __XLIBINT_HH */
#include <X11/Xos.h>
#include <X11/Sunkeysym.h>
#include "composeIM.h"

#define RmTop           (1 << 0)
#define RmBottom        (1 << 1)
#define RmLeft          (1 << 2)
#define RmRight         (1 << 3)
#define RmOverTheSpot		(1 << 5)
#define RmCenterOfScreen	(1 << 6)

typedef enum {
    RmScreen 		= 107,
    RmClient		= 108,
    RmCursor     	= 109,
    RmSpotLocation      = 110 
} RmWindowPosition;

/*
 *	Parsing File Format:
 *
 *	FILE          ::= { [PRODUCTION] [COMMENT] "\n"}
 *	PRODUCTION    ::= LHS ":" RHS [ COMMENT ]
 *	COMMENT       ::= "#" {<any character except null or newline>}
 *	LHS           ::= EVENT { EVENT }
 *	EVENT         ::= [MODIFIER_LIST] "<" keysym ">" [KEYCODE]
 *	KEYCODE	      ::= "[" keycode "]"
 *	MODIFIER_LIST ::= ("!" {MODIFIER} ) | "None"
 *	MODIFIER      ::= ["~"] modifier_name
 *	RHS           ::= ( STRING | keysym | STRING keysym )
 *	STRING        ::= '"' { CHAR } '"'
 *	CHAR          ::= GRAPHIC_CHAR | ESCAPED_CHAR
 *	GRAPHIC_CHAR  ::= locale (codeset) dependent code
 *	ESCAPED_CHAR  ::= ('\\' | '\"' | OCTAL | HEX )
 *	OCTAL         ::= '\' OCTAL_CHAR [OCTAL_CHAR [OCTAL_CHAR]]
 *	OCTAL_CHAR    ::= (0|1|2|3|4|5|6|7)
 *	HEX           ::= '\' (x|X) HEX_CHAR [HEX_CHAR]]
 *	HEX_CHAR      ::= (0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|a|b|c|d|e|f)
 *
 */

#define ENDOFFILE 0
#define ENDOFLINE 1
#define COLON 2
#define LESS 3
#define GREATER 4
#define EXCLAM 5
#define TILDE 6
#define STRING 7
#define KEY 8
#define ERROR 9
#define RIGHT_BRACKET 10
#define LEFT_BRACKET 11
#define ATTRIBUTES 12

#define T_STATE			101
#define T_END_STATE		102
#define T_SWITCH_STATE_TO	103
#define T_ATTRIBUTES		104
#define T_END_ATTRIBUTES	105
#define T_ACTIONS		106
#define T_END_ACTIONS		107
#define T_DO_CONV		108
#define T_NEXT_PAGE		109
#define T_PREV_PAGE		110
#define T_TOP_PAGE		111
#define T_LAST_PAGE		112
#define T_STATE_TYPE		113
#define T_COMPOSE_STATE		114
#define T_CODE_INPUT_STATE	115
#define T_LOOKUP_STATE		116
#define T_TABLELOOKUP		117
#define T_REMOTE_IM_STATE       151     /* Added for remote IM */
#define T_LANGUAGE		152	
#ifdef USE_CAND_SELECTION
#define T_NEXT_CAND		118
#define T_PREV_CAND		119
#define T_UP_CAND		120
#define T_DOWN_CAND		121
#define T_SELECT_CAND		122
#endif

#define ATTRIBUTE_TOKEN		200
#define A_MAX_INPUT_LENGTH	201
#define A_TYPE_OF_INPUT		202
#define A_MAX_NUMBER_OF_ROWS	203
#define A_MAX_NUMBER_OF_COLUMNS	204
#define A_MAX_NUMBER_OF_CANDIDATES	205
#define A_TITLE_STRING		206
#define A_CODE_RANGE		207
#define A_FONTSET		208
#define A_POSITION		209
#define A_PLACEMENT		210

#define EMPTY_GLYPH_CHAR 0x3f

typedef struct {
    char	*name;
    int		val;
} StateKeyWordRec;

static StateKeyWordRec keyword_tbl[] = {
    {"STATE",	T_STATE},
    {"END_STATE", T_END_STATE},
    {"STATE_TYPE", T_STATE_TYPE},
    {"SWITCH_STATE_TO", T_SWITCH_STATE_TO},
    {"#attributes", T_ATTRIBUTES},
    {"#end of attributes", T_END_ATTRIBUTES},
    {"#actions", T_ACTIONS},
    {"#end of actions", T_END_ACTIONS},
    {"DO_CONV", T_DO_CONV},
    {"NEXT_PAGE", T_NEXT_PAGE},
    {"PREV_PAGE", T_PREV_PAGE},
    {"TOP_PAGE", T_TOP_PAGE},
    {"LAST_PAGE", T_LAST_PAGE},
#ifdef USE_CAND_SELECTION
    {"NEXT_CAND", T_NEXT_CAND},
    {"PREV_CAND", T_PREV_CAND},
    {"UP_CAND", T_UP_CAND},
    {"DOWN_CAND", T_DOWN_CAND},
    {"SELECT_CAND", T_SELECT_CAND},
#endif
    {"COMPOSE", T_COMPOSE_STATE},
    {"LOOKUP", T_LOOKUP_STATE},
    {"DO_LOOKUP", T_TABLELOOKUP},
    {"CODE_INPUT", T_CODE_INPUT_STATE},
    {"REMOTE_IM", T_REMOTE_IM_STATE},
    {"LANGUAGE", T_LANGUAGE},
    {NULL, 0}
};

typedef enum {
    StringAttr,
    IntegerAttr,
    LiteralAttr
} AttribType;

typedef struct {
    char	*name;
    int		val;
    AttribType   type;
} AttribKeyWordRec;

static AttribKeyWordRec attr_tbl[] = {
    {"MAX_INPUT_LENGTH", A_MAX_INPUT_LENGTH, IntegerAttr},
    {"TYPE_OF_INPUT", A_TYPE_OF_INPUT, LiteralAttr},
    {"MAX_NUMBER_OF_ROWS", A_MAX_NUMBER_OF_ROWS, IntegerAttr},
    {"MAX_NUMBER_OF_COLUMNS", A_MAX_NUMBER_OF_COLUMNS, IntegerAttr},
    {"MAX_NUMBER_OF_CANDIDATES", A_MAX_NUMBER_OF_CANDIDATES},
    {"TITLE_STRING", A_TITLE_STRING, StringAttr},
    {"FONTSET", A_FONTSET, StringAttr},
    {"POSITION", A_POSITION, LiteralAttr},
    {"PLACEMENT", A_PLACEMENT, LiteralAttr},
    {NULL, 0, 0}
};

#define MAXSTRLEN 100

static int lastch = 0;

static int
nextch(fp)
    FILE *fp;
{
    int c;

    if (lastch != 0) {
	c = lastch;
	lastch = 0;
    } else {
	c = getc(fp);
	if (c == '\\') {
	    c = getc(fp);
	    if (c == '\n') {
		c = getc(fp);
	    } else {
		ungetc(c, fp);
		c = '\\';
	    }
	}
    }
    return(c);
}

static void
putbackch(c)
    int c;
{
    lastch = c;
}

#ifndef isalnum
#define isalnum(c)      \
    (('0' <= (c) && (c) <= '9')  || \
     ('A' <= (c) && (c) <= 'Z')  || \
     ('a' <= (c) && (c) <= 'z'))
#endif

static char tokenbuf[MAXSTRLEN];

static int
check_quoted_string(fp)
    FILE	*fp;
{
    int i, j;
    char	*p;
    int		c;
    int		token;

    p = tokenbuf;
    while ((c = nextch(fp)) != '"') {
        if (c == '\n' || c == EOF) {
    	    putbackch(c);
    	    token = ERROR;
    	    goto string_error;
        } else if (c == '\\') {
	    c = nextch(fp);
	    switch (c) {
	      case '\\':
	      case '"':
	        *p++ = c;
	        break;
	      case '0':
	      case '1':
	      case '2':
	      case '3':
	      case '4':
	      case '5':
	      case '6':
	      case '7':
	        i = c - '0';
	        c = nextch(fp);
	        for (j = 0; j < 2 && c >= '0' && c <= '7'; j++) {
			i <<= 3;
			i += c - '0';
			c = nextch(fp);
	        }
	        putbackch(c);
	        *p++ = (char)i;
	        break;
	      case 'X':
	      case 'x':
	        i = 0;
	        c = nextch(fp);
#define ishexch(c) (((c) >= '0' && (c) <= '9') || \
		    ((c) >= 'A' && (c) <= 'F') || \
		    ((c) >= 'a' && (c) <= 'f'))
	        for (j = 0; j < 2 && ishexch(c); j++) {
			i <<= 4;
			if (c >= '0' && c <= '9') {
			    i += c - '0';
			} else if (c >= 'A' && c <= 'F') {
			    i += c - 'A' + 10;
			} else {
			    i += c - 'a' + 10;
			}
			c = nextch(fp);
	        }
	        if (j == 0) {
		        token = ERROR;
		        goto string_error;
	        }
	        putbackch(c);
	        *p++ = (char)i;
#undef ishexch
	        break;
	      case '\n':
	      case EOF:
	        putbackch(c);
	        token = ERROR;
	        goto string_error;
	        /* break; */
	      default:
	        *p++ = c;
	        break;
	    }
	} else {
		*p++ = c;
	}
    }

    *p = '\0';
    token = STRING;

string_error:
    return (token);
}

static int
get_statetoken(w)
    char		*w;
{
    StateKeyWordRec	*k = keyword_tbl;

    if(w == 0 || *w == 0)
	return (-1);

    for ( ; k->name; k++) {
	if(!strcmp(k->name, w)) {
		return (k->val);
	}	
    }

    return (-1);
}

static int
get_attributetoken(w)
    char		*w;
{
    AttribKeyWordRec	*k = attr_tbl;

    if(w == 0 || *w == 0)
	return (-1);

    for ( ; k->name; k++) {
	if(!strcmp(k->name, w)) {
		return (k->val);
	}	
    }

    return (-1);
}

static int
nexttoken(fp)
    FILE *fp;
{
    int c;
    int token, state_token, attr_token;
    char *p;

    while ((c = nextch(fp)) == ' ' || c == '\t') {
    }
    switch (c) {
      case EOF:
	token = ENDOFFILE;
	break;
      case '\n':
	token = ENDOFLINE;
	break;
      case '<':
	token = LESS;
	break;
      case '[':
	token = LEFT_BRACKET;
	break;
      case ']':
	token = RIGHT_BRACKET;
	break;
      case '>':
	token = GREATER;
	break;
      case ':':
	token = COLON;
	break;
      case '!':
	token = EXCLAM;
	break;
      case '~':
	token = TILDE;
	break;
      case '"':
	token = check_quoted_string(fp);
	break;
      case '#':
	p = tokenbuf;
	*p++ = c;
	c = nextch(fp);
	while (isalnum(c) || c == ' ') {
	    *p++ = c;
	    c = nextch(fp);
	}
	*p = '\0';
	putbackch(c);
	if((state_token = get_statetoken(tokenbuf)) > 0) {
	    return (state_token);

	} else {
	    while ((c = nextch(fp)) != '\n' && c != EOF) {
	    }
	    if (c == '\n') {
		token = ENDOFLINE;
	    } else {
		token = ENDOFFILE;
	    }
        }
	break;
      default:
	if (isalnum(c) || c == '_' || c == '-') {
	    p = tokenbuf;
	    *p++ = c;
	    c = nextch(fp);
	    while (isalnum(c) || c == '_' || c == '-') {
		*p++ = c;
		c = nextch(fp);
	    }
	    *p = '\0';
	    putbackch(c);
	    token = KEY;
	    if((state_token = get_statetoken(tokenbuf)) > 0)
		return (state_token);
            if((attr_token = get_attributetoken(tokenbuf)) > 0)
                return (attr_token);
	} else {
	    token = ERROR;
	}
	break;
    }
    return(token);
}

static long
modmask(name)
    char *name;
{
    long mask;

    struct _modtbl {
	char *name;
	long mask;
    };
    struct _modtbl *p;

    static struct _modtbl tbl[] = {
	{ "Ctrl",	ControlMask	},
        { "Lock",	LockMask	},
        { "Caps",	LockMask	},
        { "Shift",	ShiftMask	},
        { "Alt",	Mod1Mask	},
        { "Meta",	Mod1Mask	},
        { "AltGraph",	Mod2Mask	},
        { NULL,		0		}};

    p = tbl;
    mask = 0;
    for (p = tbl; p->name != NULL; p++) {
	if (strcmp(name, p->name) == 0) {
	    mask = p->mask;
	    break;
	}
    }
    return(mask);
}

#define AllMask (ShiftMask | LockMask | ControlMask | Mod1Mask | Mod2Mask) 
#define LOCAL_WC_BUFSIZE 128
#define SEQUENCE_MAX	10

static LocalIMState	*
create_state(xim, name)
Ximp_XIM xim;
char	*name;
{
    XimCommon im = (XimCommon)xim;
    LocalIMState  *tmps = im->local_impart->top_state;
    LocalIMState  *lasts = 0;

    while(tmps) {
	if(!strcmp(tmps->name, tokenbuf)) {
		return 0;
	}
	lasts = tmps;
	tmps = tmps->next;
    }

    tmps = (LocalIMState*)Xmalloc(sizeof(LocalIMState));
    memset(tmps, 0, sizeof(LocalIMState));
    tmps->name = (char*)Xmalloc(strlen(name) +1);
    tmps->type = ComposeState;
    strcpy(tmps->name, name);

    if(!lasts)
    	im->local_impart->top_state = tmps;
    else
    	lasts->next = tmps;
		
    return (tmps);
}

static StateAttrib *
new_attribute()
{
	return (StateAttrib *)Xmalloc(sizeof(StateAttrib));
}

static int
parseline(fp, im, state)
FILE *fp;
XimCommon	im;
LocalIMState	**state;
{
    int token, prev_token;
    unsigned modifier_mask;
    unsigned modifier;
    unsigned tmp;
    KeySym keysym;
    DefTree *p;
    Bool exclam, tilde;
    KeySym rhs_keysym =  (KeySym)NoSymbol;
    char *rhs_string_mb = NULL;
    DefTree	**top;
    char *rhs_target_name = NULL, *ret;
    char *range_name = NULL;

    struct DefBuffer {
	unsigned modifier_mask;
	unsigned modifier;
	KeySym keysym;
	int keycode;
    };

    struct DefBuffer buf[SEQUENCE_MAX];
    int i, n;

    ActionType act = NoAction;

    do {
	token = nexttoken(fp);
    } while (token == ENDOFLINE);
    
    if (token == ENDOFFILE) {
	return(-1);
    }

    switch (token) {
	case T_STATE: {	/* begin state */
		if(*state)
			goto error;

		token = nexttoken(fp);
		if(token != STRING)
			goto error;

		*state = create_state((Ximp_XIM)im, tokenbuf);
		if(!(*state))
			goto error;
		return (0);
	        /* break; */
	}
	case T_END_STATE:
		if(!(*state))
			goto error;
		*state = 0;
		return(0);
	        /* break; */
	case T_ATTRIBUTES:
		if(!(*state) || (*state)->attrflag) 
			goto error;
		(*state)->attr = new_attribute();
		if ( (*state)->attr == NULL ) goto error;
		memset((*state)->attr, 0, sizeof(StateAttrib));
		(*state)->attrflag = True;
                return(0);
	        /* break; */
	case T_END_ATTRIBUTES:
		if(!(*state) || !(*state)->attrflag) 
			goto error;
		(*state)->attrflag = False;
                return(0);
	        /* break; */
	case T_ACTIONS:
		if(!(*state) || (*state)->actionflag) 
			goto error;
		(*state)->actionflag = True;
                return(0);
	        /* break; */
	case T_END_ACTIONS:
		if(!(*state) || !(*state)->actionflag) 
			goto error;
		(*state)->actionflag = False;
                return(0);
	        /* break; */
	case T_STATE_TYPE:
		if(!(*state)) 
			goto error;

		token = nexttoken(fp);
		if(token != COLON)
		   goto error;

		token = nexttoken(fp);
		if (token == T_LOOKUP_STATE) {
			(*state)->type = LookupState;
		} else if (token == T_CODE_INPUT_STATE) {
			(*state)->type = CodeInputState;
		} else if (token == T_REMOTE_IM_STATE) {
			(*state)->type = RemoteIMState;
		}
                return(0);
	        /* break; */
	case T_LANGUAGE:
		if(!(*state)) 
			goto error;

		token = nexttoken(fp);
		if(token != COLON)
		   goto error;

		token = nexttoken(fp);
		if (( (*state)->language = (char *)Xmalloc(strlen(tokenbuf)+1)) == NULL)
		   goto error;
            	memset((*state)->language, 0, strlen(tokenbuf)+1);
            	strcpy((*state)->language, tokenbuf);
            	return(0);
	    /* break; */
	default:
		break;
    }

    n = 0;
    prev_token = 0;
    do {
	if ((token == KEY) && (strcmp("None", tokenbuf) == 0)) {
	    modifier = 0;
	    modifier_mask = AllMask;
	    token = nexttoken(fp);
	} else if (token > ATTRIBUTE_TOKEN) {
            if ( ! (*state)->attrflag ) goto error;
	    prev_token = token;
	    token = nexttoken(fp);
	    continue;
        } else if ( token == STRING ){
            if ( ! (*state)->attrflag ) goto error;

	    if ((range_name = (char*)Xmalloc(strlen(tokenbuf) + 1)) == NULL )
		goto error;
	    memset(range_name, 0, strlen(tokenbuf)+1);
	    strcpy(range_name, tokenbuf);
	    prev_token = A_CODE_RANGE;
	    token = nexttoken(fp);
	    continue;
	} else {
	    modifier = 0;
	    modifier_mask = AllMask;
	    exclam = False;
	    if (token == EXCLAM) {
		exclam = True;
		token = nexttoken(fp);
	    }
	    while (token == TILDE || token == KEY) {
		tilde = False;
		if (token == TILDE) {
		    token = nexttoken(fp);
		    tilde = True;
		    if (token != KEY)
			goto error;
		}
		token = nexttoken(fp);
		tmp = modmask(tokenbuf);
		if (!tmp) {
		    goto error;
		}
		modifier_mask |= tmp;
		if (tilde) {
		    modifier &= ~tmp;
		} else {
		    modifier |= tmp;
		}
	    }
	    if (exclam) {
		modifier_mask = AllMask;
	    }
	}

	if (token != LESS && token != LEFT_BRACKET) {
	    goto error;
	}
	prev_token = token;

	token = nexttoken(fp);
	if (token != KEY) {
	    goto error;
	}

	token = nexttoken(fp);
	if (!(prev_token == LESS && token == GREATER)
	    && !(prev_token == LEFT_BRACKET && token == RIGHT_BRACKET)) {
	    goto error;
	}

	if (prev_token == LESS) {
	    keysym = XStringToKeysym(tokenbuf);
	    if (keysym == NoSymbol) {
		goto error;
	    }
	    buf[n].keysym = keysym;
	    buf[n].keycode = 0;	/* default */
	    buf[n].modifier = modifier;
	    buf[n].modifier_mask = modifier_mask;
	    n++;
	    if( n >= SEQUENCE_MAX )
		goto error;
	} else /* PREV_TOKEN == LEFT_BRACKET */ {
	    int keycode = atoi(tokenbuf);
	    if (n < 1) goto error; /* hopefully never happened */
	    buf[n - 1].keycode = keycode;
	    SET_KEYCODEINFO(*state);
	}
	token = nexttoken(fp);
    } while (token != COLON);

    token = nexttoken(fp);
    if ( prev_token > ATTRIBUTE_TOKEN ) {
	switch ((*state)->type) {
	case CodeInputState:
	  if ( (*state)->attr == NULL ) goto error;
	  switch (prev_token) {
	  case A_MAX_INPUT_LENGTH:
	    (*state)->attr->_attr_u.CIstruct.max_input_len = atoi(tokenbuf);
	    return(0);
	    /* break; */
	  case A_TYPE_OF_INPUT:
	    if ( !strcmp(tokenbuf, "HEX") ) {
	      (*state)->attr->_attr_u.CIstruct.type_of_input = HEX;
	    } else if ( !strcmp(tokenbuf, "OCTET") ) {
	      (*state)->attr->_attr_u.CIstruct.type_of_input = OCTET;
	    } else goto error;
	    return(0);
	    /* break; */
	  default:
	    goto error;
	    /* break; */
	  }
	  /* break; */
	case LookupState:
	  if ( (*state)->attr == NULL ) goto error;
	  switch ( prev_token ) {
	  case A_MAX_NUMBER_OF_ROWS:
	    (*state)->attr->_attr_u.LUstruct.num_of_rows = atoi(tokenbuf);
	    return(0);
	    /* break; */
	  case A_MAX_NUMBER_OF_COLUMNS:
	    (*state)->attr->_attr_u.LUstruct.num_of_cols = atoi(tokenbuf);
	    return(0);
	    /* break; */
	  case A_MAX_NUMBER_OF_CANDIDATES:
	    (*state)->attr->_attr_u.LUstruct.num_of_cands = atoi(tokenbuf);
	    return(0);
	    /* break; */
	  case A_CODE_RANGE:
	    {
	      LuRange *rangep = (*state)->attr->_attr_u.LUstruct.range;
	      int nrange = (*state)->attr->_attr_u.LUstruct.nrange;

	      if (rangep == NULL )
		rangep = (LuRange *)Xmalloc(sizeof(LuRange));
	      else
		rangep = (LuRange *)Xrealloc(rangep,
					     sizeof(LuRange)* (nrange + 1));
	      rangep[nrange].start_range = strtol(tokenbuf, &ret, 0);
	      token = nexttoken(fp);
	      rangep[nrange].end_range = strtol(tokenbuf, &ret, 0);
	      memset(rangep[nrange].RangeName, 0, sizeof(char) * 80);
	      strcpy(rangep[nrange].RangeName, range_name);
	      (*state)->attr->_attr_u.LUstruct.nrange++;
	      (*state)->attr->_attr_u.LUstruct.range = rangep;
	      Xfree(range_name);
	      return(0);
	      /* break; */
	    }
	  case A_TITLE_STRING:
	    (*state)->attr->_attr_u.LUstruct.title = (char *)Xmalloc(strlen(tokenbuf)+1);
	    strcpy((*state)->attr->_attr_u.LUstruct.title, tokenbuf);
	    return(0);
	    /* break; */
	  case A_FONTSET:
	    return(0);
	    /* break; */
	  case A_PLACEMENT:
	    return(0);
	    /* break; */
	  case A_POSITION:
	    return(0);
	    /* break; */
	  default:
	    goto error;
	    /* break; */
	  }
	  /* break; */
	default:
	  goto error;
	  /* break; */
	}
    } else if (token == STRING) {
	if( (rhs_string_mb = Xmalloc(strlen(tokenbuf) + 1)) == NULL )
	    goto error;
	memset(rhs_string_mb, 0, strlen(tokenbuf)+1);
	strcpy(rhs_string_mb, tokenbuf);
	token = nexttoken(fp);
	if (token == KEY) {
	    rhs_keysym = XStringToKeysym(tokenbuf);
	    if (rhs_keysym == NoSymbol) {
		Xfree(rhs_string_mb);
		rhs_string_mb = NULL;
		goto error;
	    }
	    token = nexttoken(fp);
	}
	if (token != ENDOFLINE && token != ENDOFFILE) {
	    Xfree(rhs_string_mb);
	    rhs_string_mb = NULL;
	    goto error;
	}
    } else if (token == KEY) {
	rhs_keysym = XStringToKeysym(tokenbuf);
	if (rhs_keysym == NoSymbol) {
	    goto error;
	}
	token = nexttoken(fp);
	if (token != ENDOFLINE && token != ENDOFFILE) {
	    goto error;
	}
	if( (rhs_string_mb = Xmalloc(1)) == NULL ) {
	    goto error;
	}
	memset(rhs_string_mb, 0, 1);
    } else if (token == T_SWITCH_STATE_TO) {
	token = nexttoken(fp);
	if(token == STRING) {
	    if((rhs_target_name = Xmalloc(strlen(tokenbuf) + 1)) == NULL){
		goto error;
	    }
	    memset(rhs_target_name, 0, strlen(tokenbuf)+1);
	    strcpy(rhs_target_name, tokenbuf);
	    act = SwitchTo;
	} else {
		goto error;
	}
    } else if (token == T_DO_CONV) {
	act = DoConv;
    } else if (token == T_TABLELOOKUP) {
	act = DoLookup;
    } else if (token == T_NEXT_PAGE) {
	act = NextPage;
    } else if (token == T_PREV_PAGE) {
	act = PrevPage;
    } else if (token == T_TOP_PAGE) {
	act = TopPage;
    } else if (token == T_LAST_PAGE) {
	act = LastPage;
#ifdef USE_CAND_SELECTION
    } else if (token == T_NEXT_CAND) {
	act = NextCand;
    } else if (token == T_PREV_CAND) {
	act = PrevCand;
    } else if (token == T_UP_CAND) {
	act = UpCand;
    } else if (token == T_DOWN_CAND) {
	act = DownCand;
    } else if (token == T_SELECT_CAND) {
	act = SelectCand;
#endif
    } else {
	goto error;
    }

    if(!(*state)) {
	*state = create_state((Ximp_XIM)im, "");
	if(!(*state))
		goto error;
    }

    if ( n == 0 ) return(0);

    top = &((*state)->parser);

    for (i = 0; i < n; i++) {
	for (p = *top; p; p = p->next) {
	    if (buf[i].keysym        == p->keysym &&
		buf[i].keycode       == p->keycode &&
		buf[i].modifier      == p->modifier &&
		buf[i].modifier_mask == p->modifier_mask) {
		break;
	    }
	}
	if (p) {
	    top = &p->succession;
	} else {
	    if( (p = (DefTree*)Xmalloc(sizeof(DefTree))) == NULL ) {
		if ( rhs_string_mb ) Xfree( rhs_string_mb );
		rhs_string_mb = NULL;
		goto error;
	    }
	    memset(p, 0, sizeof(DefTree));
	    p->keysym        = buf[i].keysym;
	    p->keycode       = buf[i].keycode;
	    p->modifier      = buf[i].modifier;
	    p->modifier_mask = buf[i].modifier_mask;
	    p->succession    = NULL;
	    p->next          = *top;
	    p->mb            = NULL;
	    p->keysym_return = NoSymbol;
	    p->action	     = NoAction;
	    p->target_name   = NULL;
	    p->target	     = NULL;
	    *top = p;
	    top = &p->succession;
	}
    }

    if( p->mb != NULL ) {
	Xfree( p->mb );
	p->mb = NULL;
    }
    if( p->target_name != NULL ) {
	Xfree( p->target_name );
	p->target_name = NULL;
    }
    if( p->target != NULL ) {
	Xfree( p->target );
	p->target = NULL;
    }

    p->action = act;

    if(rhs_target_name == NULL){
	p->mb = NULL;
	if (p->action == SwitchTo) {
	    if ( rhs_string_mb ) Xfree(rhs_string_mb);
	} else {
	    if ( rhs_string_mb ) {
		p->mb = rhs_string_mb;
	    }
	    p->keysym_return = rhs_keysym;
	}
    } else{
	p->target_name = rhs_target_name;
    }
    return(n);

error:
    while (token != ENDOFLINE && token != ENDOFFILE) {
	token = nexttoken(fp);
    }
    return(0);
}

static int
set_change_state_info(xim, p)
Ximp_XIM xim;
DefTree *p;
{
    XimCommon im = (XimCommon)xim;
    LocalIMState	*state;

    /* next */
    if (p->next) {
      if (set_change_state_info((Ximp_XIM)im, p->next) == -1)
	return -1;
    }

    /* succession */
    if (p->succession)
      return set_change_state_info((Ximp_XIM)im, p->succession);

    /* self */
    if (p->target_name == NULL)
      return 0;

    state = im->local_impart->top_state;

    while(state) {
      if (!strcmp(state->name, p->target_name)){
	p->target = state;
	break;
      }
      state = state->next;
    }
    if (state != NULL)
      return 0;
    else
      return -1;
}

int
Ximp_ParseStringFile(fp, im)
FILE *fp;
XimCommon im;
{
    int max_ev_seq = 0, i;
    LocalIMState *state = 0;

    memset(tokenbuf, 0, sizeof(tokenbuf));
    while ((i = parseline(fp, im, &state)) >= 0) {
	if (i > max_ev_seq) max_ev_seq = i;
    }

    state = im->local_impart->top_state;
    while(state) {
      if (set_change_state_info((Ximp_XIM)im, state->parser) == -1)
	return (-1);
      state = state->next;
    }

    return (max_ev_seq);
}
