/*
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.

*/
#pragma ident   "@(#)XSunIMCm.c	1.1 97/08/20        Sun Microsystems,Inc."

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

              Copyright 1990, 1991, by Sun Microsystems, Inc.

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 Sun Microsystems, Inc.
not be used in advertising or publicity pertaining to distribution
of the software without specific, written prior permission.
Sun Microsystems, Inc. makes no representations about the suitability of
this software for any purpose.  It is provided "as is" without
express or implied warranty.

Sun Microsystems Inc. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
IN NO EVENT SHALL Sun Microsystems, Inc. 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: Hideki Hiura (hhiura@Sun.COM)
	  				     Sun Microsystems, Inc.
******************************************************************/

#ifndef lint
#ident  "@(#)XSunIMCm.c	2.7    94/08/08 SMI"
#endif

/* This module uses many Solaris-specific extensions. */
#define __EXTENSIONS__

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#ifdef SVR4
#include <sys/utsname.h>
#include <sys/systeminfo.h>
#endif /* SVR4 */
#ifdef X_LOCALE
#include <X11/Xlocale.h>
#else
#include <locale.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#if XlibSpecificationRelease < 5
#include <X11/XlibR5.h>
#endif /* XlibSpecificationRelease */
#include "XSunExt.h"
#include <X11/keysym.h>
#include <X11/Xutil.h>

#ifdef sun
#include <X11/Sunkeysym.h>
#ifndef SunXK_Hangul
#define SunXK_Hangul		0xFF31
#endif
#ifndef SunXK_Hangul_Hanja
#define SunXK_Hangul_Hanja	0xFF34
#endif
#endif

#ifdef SVR4 
#include <libintl.h>
#endif /* SVR4 */

#include "mle/cm.h"
#include "mle/env.h"
#include "mle/cmkeys.h"
#include <pwd.h>
#include <grp.h>
#include "XSunIMProt.h"
#include "XSunIMPriv.h"
#include "XSunIMMMan.h"
#include "XSunIMMthd.h"
#include "XSunIMCm.h"
#include "XSunIMPub.h"

#define CM_IF_VERSION "1.26"

Public int i18nXView2_COMPAT ;

#define	IMS_CM_NEGO_GET		1
#define	IMS_CM_NEGO_SET 	2

#ifndef ENV_SET_SIMPLE_MODE
/*
 * This is for ALE support. JLE header file does not contains this
 * declaration.
 * ENV_SET_SIMPLE_MODE((e_char *)ce_string) sets the mode to ce_string,
 * ENV will display the corresponding string for the mode.
 */
#	define ENV_SET_SIMPLE_MODE     117
#endif

Private Bool             iml_cm_OpenIF      (iml_if_t *, char*, void*);
Private Bool             iml_cm_CloseIF     (iml_if_t *);
Private Bool             iml_cm_GetIFValue  (iml_if_t *, char *, void*);
Private Bool             iml_cm_SetIFValue  (iml_if_t *, char *, void*);
Private void *           iml_cm_CreateSC    (iml_if_t *, void *, char*);
Private Bool             iml_cm_DestroySC   (iml_session_t*);
Private Bool             iml_cm_SetSCValue  (iml_session_t*, char *, void*);
Private Bool             iml_cm_GetSCValue  (iml_session_t*, char *, void*);
Private char *           iml_cm_ResetSC     (iml_session_t*);
Private void             iml_cm_SetSCFocus  (iml_session_t*);
Private void             iml_cm_UnsetSCFocus(iml_session_t*);
Private void             iml_cm_SendEvent   (iml_session_t*, XKeyEvent*);
Private Bool             iml_cm_FilterEvent (XEvent*);

#ifdef DYNAMICIF
Private
#else
Public
#endif  
if_methods_t cm2_methods = {
    iml_cm_OpenIF,      
    iml_cm_CloseIF,     
    iml_cm_GetIFValue,  
    iml_cm_SetIFValue,  
    iml_cm_CreateSC,    
    iml_cm_DestroySC,   
    iml_cm_SetSCValue,  
    iml_cm_GetSCValue,  
    iml_cm_ResetSC,     
    iml_cm_SetSCFocus,  
    iml_cm_UnsetSCFocus,
    iml_cm_SendEvent,
    iml_cm_FilterEvent,
    NULL			/* if_SetAuxValues */
} ;

Private Bool
iml_cm_OpenIF(If, unused_char, unused_data)
iml_if_t *If ;
char *unused_char;
void *unused_data;
{
    cm_opsw_init();
    return True ;
}
Private Bool
iml_cm_CloseIF(If)
iml_if_t *If ;
{
    return True;
}
Private Bool
iml_cm_GetIFValue(If, attr, value)
iml_if_t *If ;
char *attr;
void *value;
{
    if(!strcmp(attr, IF_VERSION)){
	*(char**)value = CM_IF_VERSION ;
	return True ;
    }
    return False ;
}
Private Bool
iml_cm_SetIFValue(If, attr, value)
iml_if_t *If ;
char *attr;
void *value;
{
    return False ;
}
Private void * 
iml_cm_CreateSC(If, ic, le_name)
iml_if_t *If ;
void *ic ;
char *le_name ; /* This is for multiple language engine per I/F */
{
    Private int cm_create_session(/*int id,iml_session_t *s,Bool is_recycled*/);
    int le_id;
    iml_session_t *s ;

    if(le_name == NULL){
	le_id = 0 ;
    } else {
	le_id = atoi(le_name);
    }
    return (void *) If->m->iml_construct_session(If, ic, le_id,
						 sizeof(iml_session_t),
						 cm_create_session);
}
Private Bool
iml_cm_DestroySC(s)
iml_session_t *s ;
{
    cm_destruct_session(s);
}
Private char * 
iml_cm_ResetSC(s)
iml_session_t *s ;
{
    cm_send_commit(s);
}

Private Bool
iml_cm_SetSCValue(s, attr, value)
iml_session_t *s ;
char *attr ;
void *value ;
{
    if(!strcmp(attr, SC_Henkan_Mode) || !strcmp(attr, XNExtXimp_Conversion)){
	if((Bool)value == True){
	    if(!(s->public_status & IMLSTATUS_Henkan_Mode)){
		s->public_status |= IMLSTATUS_Henkan_Mode ;
		cm_make_conversion_on(s);
	    }
	} else {
	    if(s->public_status & IMLSTATUS_Henkan_Mode){
		s->public_status &= ~IMLSTATUS_Henkan_Mode ;
		cm_make_conversion_off(s);
	    }
	}
	return True ;
    } else if(!strcmp(attr, SC_STATUS)){
	if(!(s->public_status & IMLSTATUS_Henkan_Mode)){
	    if((iml_status_t)value & IMLSTATUS_Henkan_Mode){
		cm_make_conversion_on(s);
	    }
	}
	s->public_status = (iml_status_t)value ;
	return True ;
    } else {
	if(ims_cm_negotiation_pool(IMS_CM_NEGO_SET, s, attr, value))
		return True;
	else
		return False;
    }
}

Private Bool
iml_cm_GetSCValue(s, attr, value)
iml_session_t *s ;
char *attr ;
void *value ;
{
    if(!strcmp(attr, SC_STATUS)){
	iml_inst *lp = s->If->m->iml_make_status_notify_inst(s);
	*(iml_status_t*)value = s->public_status ;
	if(lp = s->If->m->iml_execute(s, &lp)){
	    cm_eval_results(s, lp);
	}
	return True ;
    }
    /* SetSCValue sets the value in negotiation pool and get should get
       it from negot pool ??? */
    return ims_cm_negotiation_pool(IMS_CM_NEGO_GET, s, attr, value);
    
}
Private void
iml_cm_SetSCFocus(s)
iml_session_t *s ;
{
    iml_inst *lp = s->If->m->iml_make_status_notify_inst(s);
    if(lp = s->If->m->iml_execute(s, &lp)){
	cm_eval_results(s, lp);
    }
}
Private void
iml_cm_UnsetSCFocus(s)
iml_session_t *s ;
{
    iml_inst *lp = s->If->m->iml_make_status_notify_inst(s);
    if(lp = s->If->m->iml_execute(s, &lp)){
	cm_eval_results(s, lp);
    }
}
    
Private Bool translate_keysym_to_cm_keycode(/*s, keysym, cm_keycode*/) ;

#define cm_is_conv_on_key(s,l) ((l)>0 && ((s)->conversion_on_key == *((s)->XLookupBuf)))
Private void
iml_cm_SendEvent(s, e)
iml_session_t *s ;
XKeyEvent *e;  /* If this is NULL, this event must be pseudo event */
{
    char localename[LOCALENAMELEN];
    Private struct cm_to_env cte ;
    Private struct cm_to_env *ctep = &cte ;
    Private struct env_to_cm etc ;
    int l ; /* length of Lookuped string */
    iml_inst *lp ;
    XComposeStatus NotSuppoted ;
    unsigned int   save_state;
    
    /*
     * jle103 engine does not use KeyPress Event
     */
    if(e && e->type != KeyPress){
	return ; 
    }
    if(e){
	s->event = e ; /* save current KeyEvent */
    } else {
	e = s->event ; /* May NOT be needed: restore KeyEvent */
    }

    save_state = e->state;
    if(s->public_status & IMLSTATUS_Henkan_Mode && e->state & ControlMask) {
	if (e->display)
	    e->state &= ~e->display->mode_switch;
    }
    l= s->If->m->iml_lookupString(e, (char*)s->XLookupBuf,s->XLookupBuflen,&s->keysym);
    e->state = save_state;

    if(! (s->public_status & IMLSTATUS_Henkan_Mode)){
	/*
	 * in case when conversion mode is off
	 */
	iml_inst *rv ;
	if((s->keysym == XK_Henkan_Mode) || 
	   (s->keysym == SunXK_Hangul) || cm_is_conv_on_key(s,l)){
	    cm_make_conversion_on(s);
	    return ;
	} 
	lp = s->If->m->iml_make_keypress_inst(s, (char*)s->XLookupBuf) ;
	if(lp = s->If->m->iml_execute(s, &lp)){
	    cm_eval_results(s, lp);
	}
	return ;
    }

    if(s->whoIsMaster == CBIsMaster && IS_REGION_ACTIVE(s, LOOKUP)){
	lp = s->If->m->iml_make_lookup_process_inst(s, True, 0);
	if(lp = s->If->m->iml_execute(s, &lp)){
		cm_eval_results(s, lp);
	}
	return;
    }
	
    /*
     * in case when conversion mode is on
     */
    if(translate_keysym_to_cm_keycode(s, s->keysym, &etc.ec_key, l)){
	iml_inst **rrv ;
	etc.ec_operation = CM_SIMPLE_EVENT ;
	etc.ec_next = NULL ;
	ctep = cm_put(s->language_engine, s->session_id, &etc);
	eval_cm_to_env_packet(s, ctep);
	return;
    } else {
	/*
	 * send keypress, but do not what will happen as the results.
	 */
	lp = s->If->m->iml_make_keypress_inst(s, (char*)s->XLookupBuf) ;
	if(lp = s->If->m->iml_execute(s, &lp)){
	    cm_eval_results(s, lp);
	}
	return;
    }
}

Private Bool
iml_cm_FilterEvent(e)
XEvent *e ;
{
    return False ;
}

Private void
cm_conversion_on(s)
iml_session_t *s;
{
    iml_inst *rv ;

    if(s->remainder){
	rv = s->remainder ;
	s->remainder = NULL ;
	return;
    }
    cm_make_conversion_on(s);
}

Private void
cm_eval_results(s, results)
iml_session_t *s;
iml_inst *results ;
{
    XIMLookupStartCallbackStruct *ls;
    XIMLookupProcessCallbackStruct *lp;
    Private struct cm_to_env cte ;
    int *i ;
    
    if(results->opcode & IMM_CB_RESULT_REQUIRED){
	results->opcode &= ~IMM_CB_RESULT_REQUIRED ;
	switch(results->opcode){
	  case IMM_LOOKUP_START:
	    ls = (XIMLookupStartCallbackStruct *)& results->operand ;
	    if(s->whoIsMaster == HasNotBeenNegotiated) {
	      s->whoIsMaster =  ls->whoIsMaster ;
	    } else {
		break;
	    }
	    /*
	     * Obey CBPreferrence
	     */
	    s->XIMPreference = s->CBPreference ;
	    break;
	  case IMM_LOOKUP_PROCESS:
	    lp = (XIMLookupProcessCallbackStruct *)& results->operand ;
            switch(lp->index_of_choice_selected) {
                case XIM_UNDETERMINED:
                case XIM_UNKNOWN_KEYSYM:
                  /*
                   * invalid label input is done.
                   */
                        break;
                case XIM_UNKNOWN_CONTROL:
                        if(s->whoIsMaster == CBIsMaster)
                                cm_select_unknown_control(s);
                        break;
                default:
                        cm_set_choice(s, lp->index_of_choice_selected);
            }
	    return;
	case IMM_PREEDIT_START:
	    s->PreEditBufferSizeInCallback = *(int *)&results->operand ;
	    break;
	  default:
	    break;
	}
	return ;
    }
}

Private void
cm_make_conversion_off(s)
iml_session_t *s ;
{
    cm_send_commit(s);
}
Private void
cm_make_conversion_on(s)
iml_session_t *s ;
{
    iml_inst *rv = NULL;
    struct cm_to_env *ctep ;
    struct env_to_cm *etcp ;
    iml_inst *lp ;
    
    s->public_status |= IMLSTATUS_Henkan_Mode ;
    etcp = (struct env_to_cm *)_iml_new(s, sizeof(struct env_to_cm));
    bzero(etcp, sizeof(struct env_to_cm));
    etcp->ec_operation = CM_CMON ;
    etcp->ec_next = NULL ;
    ctep = cm_put(s->language_engine, s->session_id, etcp);
    
    lp = s->If->m->iml_make_status_notify_inst(s);
    s->If->m->iml_link_inst_tail(&rv, lp);
    lp = s->If->m->iml_make_preedit_start_inst(s);
    s->If->m->iml_link_inst_tail(&rv, lp);
    if(lp = s->If->m->iml_execute(s, &rv)){
	cm_eval_results(s, lp);
    }
    eval_cm_to_env_packet(s, ctep);
}
Private int
cm_create_session(cm_id, s, is_recycled)
int cm_id ;
iml_session_t *s ;
Bool is_recycled ;
{
    struct cm_initstruct cm_initvalue ;
    struct cm_to_env *cte ;
    struct env_to_cm etc;
    int session_id ;
    cmif_spec_t    *if_spec = (cmif_spec_t *)s->If->if_spec ;
    
    construct_cm_initial_data(& cm_initvalue);
    if((session_id = cm_open(cm_id, & cm_initvalue, &cte)) == -1){
#ifdef SVR4
        fprintf(stderr,dgettext("SUNW_XIM_HTT_MSG",
                 "cm I/F: language engine (id %d) open failed.\n"), cm_id);
#else /* SVR4 */
        fprintf(stderr,"cm I/F: language engine (id %d) open failed.\n",cm_id);
#endif /* SVR4 */
	return(-1);
    }
    if(!if_spec) {
	if((s->If->if_spec = (void*)calloc(1,sizeof(cmif_spec_t))) == NULL){
#ifdef SVR4
            fprintf(stderr,dgettext("SUNW_XIM_HTT_MSG", "htt: exceeds memory high water\n"));
#else /* SVR4 */
            fprintf(stderr,"htt: exceeds memory high water\n");
#endif /* SVR4 */
	    exit(1) ;
	}
	init_status_string(s);
    } 
    eval_cm_to_env_packet(s, cte);

    ims_cm_negotiation_init(s, &etc);
    if(!cm_initvalue.env_value) {
    	(void)cm_put(cm_id, session_id, &etc);
	s->nego = True;
    }
    else {
	s->nego = False;
    }
    ims_cm_negotiation_resolve(s, &etc);

    return(session_id);
}

Private int
construct_cm_initial_data(cm_initp)
struct cm_initstruct *cm_initp;
{
    struct group   *gp;
    struct passwd  *pp;
    Private char     host_name[64];
#ifdef SVR4
    static char     id_string[64];
#endif /* SVR4 */    

#define	ENV_VERSION	2
    cm_initp->env_value = (e_char *) ENV_VERSION;

    cm_initp->usr_auth_info.uid = getuid();
    cm_initp->usr_auth_info.gid = getgid();
#ifdef SVR4
    sysinfo(SI_HW_SERIAL, id_string, sizeof(id_string));
    cm_initp->usr_auth_info.hid = atoi(id_string);
#else
    cm_initp->usr_auth_info.hid = gethostid();
#endif /* SVR4 */    
    pp = getpwuid(getuid());
    if (pp != (struct passwd *)NULL)
      cm_initp->usr_auth_info.user_name = pp->pw_name;
    else
      cm_initp->usr_auth_info.user_name = (char *)NULL;
    gp = getgrgid(getgid());
    if (gp != (struct group *)NULL)
      cm_initp->usr_auth_info.grp_name = gp->gr_name;
    else
      cm_initp->usr_auth_info.grp_name = (char *)NULL;
#ifdef SVR4
    sysinfo(SI_HOSTNAME, host_name, sizeof(host_name));
#else  
    gethostname(host_name, sizeof(host_name));
#endif /* SVR4 */
    cm_initp->usr_auth_info.host_name = host_name;

}

Private Bool
ims_cm_negotiation_pool(op, s, attr, value)
int	      op;
iml_session_t *s ;
char	      *attr;
void	      *value;
{
    static ImsCmNegotiation   nego;
    static LucNegotiation     luc;
    static int		      nrows = 0;
    static int		      ncols = 0;

    nego.luc = &luc;
    switch (op) {
	case IMS_CM_NEGO_GET:
		if(!strcmp(attr, SC_nego_data)) {
			*(ImsCmNegotiation **)value = &nego;
			return True;
		}
		if(!strcmp(attr, SC_lookup_ncols)) {
			*(int *)value = ncols;
			return True;
		}
		if (!strcmp(attr, SC_lookup_nrows)) {
			*(int *)value = nrows;
			return True;
		}
		if(!strcmp(attr, SC_lookup_root)) {
		  *(int *) value = luc.luc_is_rootwindow;
		  return True;
		}
		if(!strcmp(attr, SC_lookup_imscontrol)) {
		  *(Bool *) value = luc.ims_takes_control;
		  return True;
		}
		printf ("Unknown option %s\n", attr);
		return False;
	case IMS_CM_NEGO_SET:
		if(!strcmp(attr, SC_lookup_root))
			luc.luc_is_rootwindow = (int)value;
		else if(!strcmp(attr, SC_lookup_imscontrol))
			luc.ims_takes_control = (int)value;
		else if(!strcmp(attr, SC_lookup_labeltype)) {
		  	switch ( (int)value ) {
			   case IM_LUC_LABEL_NONE:
				luc.label_type  = LUC_LABEL_NONE;
				break;
			   case IM_LUC_LABEL_NUMERIC:
				luc.label_type  = LUC_LABEL_NUMERIC;
				break;
			   case IM_LUC_LABEL_ALPHABETIC:
				luc.label_type  = LUC_LABEL_ALPHABETIC;
				break;
			   case IM_LUC_LABEL_ALPHA_UPPER:
				luc.label_type  = LUC_LABEL_ALPHA_UPPER;
				break;
			}
		}
		else if(!strcmp(attr, SC_lookup_nchoices))
			luc.choice_per_window = (int)value;
		else if(!strcmp(attr, SC_lookup_rootwidth))
			luc.root_width = (int)value;
		else if(!strcmp(attr, SC_lookup_lines))
			luc.root_lines = (int)value;
		else if(!strcmp(attr, SC_lookup_ewidth))
			luc.e_width_per_can = (int)value;
		else if(!strcmp(attr, SC_lookup_cwidth))
			luc.max_width_per_car = (int)value;
		else if(!strcmp(attr, SC_lookup_nrows))
			nrows = (int)value;
		else if(!strcmp(attr, SC_lookup_ncols))
			ncols = (int)value;
		else if(!strcmp(attr, SC_nego_flush))
			if((Bool)value == True)
				ims_cm_luc_negotiation(s);
		else if(!strcmp(attr, SC_lookup_direction))
			;	/* nothing to do */
		else if(!strcmp(attr, SC_lookup_whoownslabel))
			;	/* nothing to do */
		else
			return False;
		return True;
	default:
		return False;

    }
}

Private
ims_cm_negotiation_init(s, etcp)
iml_session_t *s;
struct env_to_cm *etcp;
{
    iml_inst *lp ;
    ImsCmNegotiation	*negop;
 
    (void)ims_cm_negotiation_pool(IMS_CM_NEGO_GET, s, SC_nego_data, &negop);
    bzero(etcp, sizeof(struct env_to_cm));
    etcp->ec_operation = CM_IMSERVER_NEGOTIATION;
    etcp->ec_parameter.ep_string.e_keyptr = (e_char *)(negop);
    negop->cm_interested = 0;
}

Private
ims_cm_negotiation_resolve(s, etcp)
iml_session_t *s;
struct env_to_cm *etcp;
{
    ImsCmNegotiation	
	*negop = (ImsCmNegotiation *)etcp->ec_parameter.ep_string.e_keyptr;
    char	*locale;

   /*
    *  Fake!. Wrapper libmle in ja locale assumes that CM_SELECT should
    *  be the very first packet once it gets into a selection region.
    *  If we send some other packet before CM_SELECT, libmle dies.
    *  Thus we pretend that the wrapper libmle responded to this 
    *  negotiation and it preferred IM to take control of LUC.
    */
    locale = setlocale(LC_CTYPE, NULL);
    if(!strncmp(locale, "ja", 2)) {
	negop->cm_interested |= IM_CM_LUC_NEGOTIATED;
	negop->luc->ims_takes_control = 1;
    }

    if(negop->cm_interested & IM_CM_LUC_NEGOTIATED) {
	int	tmp_ltype;
	switch(negop->luc->label_type) {
		case LUC_LABEL_NONE:
			tmp_ltype = IM_LUC_LABEL_NONE;
			break;
		case LUC_LABEL_ALPHABETIC:
			tmp_ltype = IM_LUC_LABEL_ALPHABETIC;
			break;
		case LUC_LABEL_NUMERIC:
			tmp_ltype = IM_LUC_LABEL_NUMERIC;
			break;
		case LUC_LABEL_ALPHA_UPPER:
			tmp_ltype = IM_LUC_LABEL_ALPHA_UPPER;
			break;
		default:  /* Must be an error within cm_put() */
			tmp_ltype = IM_LUC_LABEL_ALPHABETIC;
			break;
	}
	if(negop->luc->ims_takes_control) {
	   /* Things work with CBPreference */
		s->whoIsMaster = CBIsMaster;
		s->CBPreference.WhoOwnsLabel = CBOwnsLabel;
		s->CBPreference.choice_per_window = negop->luc->choice_per_window;
		s->CBPreference.label_type = tmp_ltype;
		/* Other fieleds should not be changed by cm_put() */
               (void)ims_cm_negotiation_pool(IMS_CM_NEGO_GET, s, SC_lookup_ncols,
				&(s->CBPreference.ncolumns));
               (void)ims_cm_negotiation_pool(IMS_CM_NEGO_GET, s, SC_lookup_nrows,
				&(s->CBPreference.nrows));
	}
	else {
	   /* Things work with XIMPreference */
		s->whoIsMaster = XIMIsMaster;
		s->XIMPreference.choice_per_window = negop->luc->choice_per_window;
		s->XIMPreference.label_type = tmp_ltype;
		/* Followings are the copy of CBPreference data */
		s->XIMPreference.WhoOwnsLabel = CBOwnsLabel;
               (void)ims_cm_negotiation_pool(IMS_CM_NEGO_GET, s, SC_lookup_ncols,
				&s->XIMPreference.ncolumns);
               (void)ims_cm_negotiation_pool(IMS_CM_NEGO_GET, s, SC_lookup_nrows,
				&s->XIMPreference.nrows);
	}
    }
    else {
      /* Things work with XIMPreference */
	(void)ims_cm_negotiation_pool(IMS_CM_NEGO_GET, s, SC_nego_data, &negop);
	s->whoIsMaster = XIMIsMaster;
	s->XIMPreference.choice_per_window = negop->luc->choice_per_window;
        s->XIMPreference.label_type  = negop->luc->label_type;
        s->XIMPreference.WhoOwnsLabel = CBOwnsLabel;
        (void)ims_cm_negotiation_pool(IMS_CM_NEGO_GET, s, SC_lookup_ncols,
				&s->XIMPreference.ncolumns);
        (void)ims_cm_negotiation_pool(IMS_CM_NEGO_GET, s, SC_lookup_nrows,
				&s->XIMPreference.nrows);
    }

}

ims_cm_luc_negotiation(s)
iml_session_t	*s;
{
    struct env_to_cm etc;

    ims_cm_negotiation_init(s, &etc);
    if(s->nego)
    	(void)cm_put(s->language_engine, s->session_id, &etc);
    ims_cm_negotiation_resolve(s, &etc);
}

Private 
env_commit(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);
    int len;
    if(i18nXView2_COMPAT){
	lp = s->If->m->iml_make_preedit_erase_inst(s);
	s->If->m->iml_link_inst_tail(rrv, lp);
	lp = s->If->m->iml_make_preedit_done_inst(s);
	s->If->m->iml_link_inst_tail(rrv, lp);
    }    
    /*
     * Only multi byte format can be used for commit string.
     */
    if (rp->ce_string) {
	len = strlen((const char*)rp->ce_string);
    } else {
	len = 0;
    }
    lp = s->If->m->iml_make_commit_inst(s, len, rp->ce_string);
    s->If->m->iml_link_inst_tail(rrv, lp);
}

Private 
env_set_current_region(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    iml_inst *results ;
    struct cm_to_env *rp = get_cte(ctep);
    
    switch(rp->ce_region){
    case INTERM_REGION:
	if(!IS_REGION_ACTIVE(s, PREEDIT)){
	    ACTIVATE_REGION(s, PREEDIT) ;
	    lp = s->If->m->iml_make_preedit_start_inst(s);
	    if(lp = s->If->m->iml_execute(s, &lp)){
		cm_eval_results(s, lp);
	    }
	}
	lp = s->If->m->iml_make_nop_inst(s);
	break;
    case MISC_REGION:
	/*
	 * postpone start.
	 */
	_imm_trace("IMM_SET_REGION(AUX)changed to NOP",0,0);
	lp = s->If->m->iml_make_nop_inst(s);
	break ;
    case SELECT_REGION:
	if(!IS_REGION_ACTIVE(s, LOOKUP)){
	    ACTIVATE_REGION(s, LOOKUP) ;
	    lp = s->If->m->iml_make_lookup_start_inst(s);
	    if(lp = s->If->m->iml_execute(s, &lp)){
		cm_eval_results(s, lp);
	    }
	}
	lp = s->If->m->iml_make_nop_inst(s);
	break ;
    case MODE_REGION:
	if(IS_REGION_ACTIVE(s, STATUS )) {
	    lp = s->If->m->iml_make_nop_inst(s);
	} else {
	    ACTIVATE_REGION(s, LOOKUP) ;
	    lp = s->If->m->iml_make_status_start_inst(s);
	}
	break;
    }
    if(lp) lp->next = NULL ;
    *rrv = lp;
}
/*------------------------------------------------------------------*/

Private 
env_select_prev(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);

    if(!IS_REGION_ACTIVE(s, LOOKUP)){
	ACTIVATE_REGION(s, LOOKUP) ;
	lp = s->If->m->iml_make_lookup_start_inst(s);
	if(lp = s->If->m->iml_execute(s, &lp)){
	    cm_eval_results(s, lp);
	}
    }
    lp = s->If->m->iml_make_lookup_process_inst(s, False, 0);
    s->If->m->iml_link_inst_tail(rrv, lp);
    *rrv = lp;
}
Private 
env_cm_off(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    iml_status_t *status ;
    struct cm_to_env *rp = get_cte(ctep);
    
    s->public_status &= ~IMLSTATUS_Henkan_Mode;
    lp = s->If->m->iml_make_status_notify_inst(s);
    s->If->m->iml_link_inst_tail(rrv, lp);
    lp = s->If->m->iml_make_preedit_done_inst(s);
    s->If->m->iml_link_inst_tail(rrv, lp);

    _imm_trace("IMM_SET_STATUS",0,*status);
}
Private 
env_setkey_cm_on(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);

    lp = s->If->m->iml_make_nop_inst(s); 
    s->If->m->iml_link_inst_tail(rrv, lp);
    /*
     * Since every thing is done inside imlogic, IMM_GET_KEY is
     * now obsolete. conv_on_key is stored as mbchar in session.
     */
    s->conversion_on_key = rp->ce_rtn_value;/* In multi byte form */
    _imm_trace("IMM_NOP(ON key is stored)",s->conversion_on_key,0);
}
Private 
env_nop(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);

    _imm_trace("IMM_NOP(ENV_NOP)",0,0);
    lp = s->If->m->iml_make_nop_inst(s);
    *rrv = lp ;
}
Private 
env_interm_reset(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);

    _imm_trace("IMM_RESET",0,0);
    lp=s->If->m->iml_make_preedit_erase_inst(s);
    s->If->m->iml_link_inst_tail(rrv, lp);
}    
Private iml_inst *
make_preedit_draw_inst(s, rp)
iml_session_t *s;
struct cm_to_env *rp ;
{
    char    *cmbs = (char *) rp->ce_text ;                  /* current mbstr */
    int      cmbl = strlen(cmbs);                           /* current mblen */
    wchar_t *cws ;                                          /* current wstr */
    int      cwl ;                                          /* current len */
    XIMFeedback *cfb ;                                      /* current fb */

    cws = (wchar_t*)_iml_new(s, (cmbl+1)*sizeof(wchar_t));
    mbstowcs(cws,cmbs,IML_MAX_SLOT_SIZE);
    cwl = wslen(cws);
    cfb = _iml_new(s, (cmbl + 1)*sizeof(XIMFeedback));
    cm_attr2feedback(cfb, rp->ce_text_attr, cwl, cmbs);
#ifndef NO_COLOR_FEEDBACK
    /* Currently, cm/env spec doesn't have color text feedback.
     * Up until there is a color feedback, NULL feedback will be
     * used. 12/5/93 
     *
     * cm_colorattr2colortextfeedback(cffg, cfbg, rp->ce_text_attr, cwl, cmbs);
     */
    return s->If->m->iml_make_preedit_draw_inst(s, cws, cfb, 
	(XIMTextColorFeedback *)NULL, (XIMTextColorFeedback *)NULL);
#else
    return s->If->m->iml_make_preedit_draw_inst(s, cws, cfb);
#endif  /* NO_COLOR_FEEDBACK */
}

Private 
env_interm(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);
    
    if(!IS_REGION_ACTIVE(s, PREEDIT)){
	ACTIVATE_REGION(s, PREEDIT) ;
	lp = s->If->m->iml_make_preedit_start_inst(s);
	if(lp = s->If->m->iml_execute(s, &lp)){
	    cm_eval_results(s, lp);
	}
    }
    lp = make_preedit_draw_inst(s, rp);
    s->If->m->iml_link_inst_tail(rrv, lp);
    _imm_trace("IMM_SET_REGION(pseudo PREEDIT)",0,0);
}
Private 
env_m_interactive(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
#ifdef IMM_AUX_DRAW
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);
    
    lp = make_preedit_draw_inst(s, rp);
    lp->opcode = IMM_AUX_DRAW ;
    _imm_trace("IMM_PREEDIT_DRAW(Draw All)",wc_text,p->text.length);
    s->If->m->iml_link_inst_tail(rrv, lp);
#endif
}

Private 
env_select_reset(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);

    _imm_trace("IMM_RESET",0,0);
    lp = (iml_inst*)s->If->m->iml_make_lookup_done_inst(s);
    s->If->m->iml_link_inst_tail(rrv, lp);
}    

Private 
env_select(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);
    
    if(!IS_REGION_ACTIVE(s, LOOKUP)){
	ACTIVATE_REGION(s, LOOKUP) ;
	lp = s->If->m->iml_make_lookup_start_inst(s);
	s->If->m->iml_link_inst_tail(rrv, lp);
    }
    lp = s->If->m->iml_make_lookup_draw_inst(s, rp->ce_candidate, rp->ce_ncandidate);
    s->If->m->iml_link_inst_tail(rrv, lp);
    _imm_trace("PSEUDO LOOKUP",0,0);
}

Private 
env_select_end(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);

    lp = s->If->m->iml_make_lookup_done_inst(s);
    s->If->m->iml_link_inst_tail(rrv, lp);
    INACTIVATE_REGION(s, LOOKUP) ;
}
Private 
env_select_next(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);

    if(!IS_REGION_ACTIVE(s, LOOKUP)){
	ACTIVATE_REGION(s, LOOKUP) ;
	lp = s->If->m->iml_make_lookup_start_inst(s);
	if(lp = s->If->m->iml_execute(s, &lp)){
	    cm_eval_results(s, lp);
	}
    }
    lp = s->If->m->iml_make_lookup_process_inst(s, False, 0);
    s->If->m->iml_link_inst_tail(rrv, lp);
}
Private 
env_select_commit(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);
    
    lp = s->If->m->iml_make_lookup_process_inst(s, True, 0);
    s->If->m->iml_link_inst_tail(rrv, lp);
    _imm_trace("IMM_LOOKUP_PROCESS",0,0);
}
Private 
_env_set_mode(s, rrv, ctep, simple)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
Bool simple;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);
    wchar_t *ws ;
    XIMFeedback *fb ;
    
    if(!IS_REGION_ACTIVE(s, STATUS)){
	ACTIVATE_REGION(s, STATUS) ;
	lp = s->If->m->iml_make_status_start_inst(s);
	s->If->m->iml_link_inst_tail(rrv, lp);
    }
    ws = s->status_cache.ws ;
    if(cm_make_status_string(s, ws, (unsigned char *)rp->ce_mode_list, simple)>0){
	register i, l = wslen(s->status_cache.ws) ;
	
	/*
	 * could be re-allocated in cm_make_status_string
	 */
	ws = s->status_cache.ws ;
	fb = (XIMFeedback *)_iml_new(s, wslen(ws)*sizeof(XIMFeedback));    
	for(i = 0 ; i < l ; i++)
	    s->status_cache.sfb[i] = s->status_cache.fb[i] | XIMTertiary ; /* for stipple */
#ifndef NO_COLOR_FEEDBACK
	if(s->private_status & BEING_FOCUSED)
	    lp = s->If->m->iml_make_status_draw_inst(s, ws, fb,
		s->status_cache.cffg, s->status_cache.cfbg);
	else
	    lp = s->If->m->iml_make_status_draw_inst(s, ws, s->status_cache.sfb,
		s->status_cache.scffg, s->status_cache.scfbg);
#else
	if(s->private_status & BEING_FOCUSED)
	    lp = s->If->m->iml_make_status_draw_inst(s, ws, fb);
	else
	    lp = s->If->m->iml_make_status_draw_inst(s,ws,s->status_cache.sfb);
#endif  /* NO_COLOR_FEEDBACK */
	s->If->m->iml_link_inst_tail(rrv, lp);
    } else {
	s->status_cache.ws = (wchar_t *) "\00\00\00\00" ;
    }
    _imm_trace("IMM_STATUS_DRAW",ws,t->length);
}
Private
env_set_mode(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    _env_set_mode(s, rrv, ctep, 0);
}
Private
env_set_smode(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    _env_set_mode(s, rrv, ctep, 1);
}
Private 
env_misc_reset(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);
#ifdef IMM_AUX_DONE
/*
 * NOT IMPLEMENTED YET
 */
    _imm_trace("IMM_NOP(ENV_MISC_RESET)",0,0);
    lp=(iml_inst*)_iml_new(s, sizeof(iml_inst));
    lp->opcode = IMM_AUX_DONE ;
    lp->next = NULL ;
    s->If->m->iml_link_inst_tail(rrv, lp);
#endif
}    

Private 
env_m_label(s, rrv, ctep)
iml_session_t *s;
iml_inst **rrv;
struct cm_to_env **ctep;
{
    iml_inst *lp ;
    struct cm_to_env *rp = get_cte(ctep);
#ifdef IMM_AUX_START
    XIMAuxStartCallbackStruct *q ;
    XIMText *t;
    int l ;

    _imm_trace("IMM_AUX_START(ENV_M_LABEL)",0,0);
    _iml_put_current_region(s, AUX) ;
    lp=(iml_inst*)_iml_new(s, sizeof(iml_inst)+
			   sizeof(XIMAuxStartCallbackStruct));
    lp->opcode = IMM_AUX_START ;
    lp->next = NULL ;
    q = (XIMAuxStartCallbackStruct *)&lp->operand ;
    l = strlen(rp->ce_misc_text)*sizeof(wchar_t);
    q->event = s->event ;
    t = q->label = _iml_new(s, sizeof(XIMText));
    t->encoding_is_wchar = True ;
    t->string.wide_char = _iml_new(s, l);
    t->length = mbstowcs(t->string.wide_char, (char *)rp->ce_misc_text, l)
    		         *sizeof(wchar_t);
    t->feedback = _iml_new(s, t->length);
#ifdef SVR4
    memset(t->feedback, 0, t->length);
#else
    bzero(t->feedback, t->length);
#endif /* SVR4 */    
    t = q->text = _iml_new(s, sizeof(XIMText));	      
    t->length = 0 ;
    s->If->m->iml_link_inst_tail(rrv, lp);
#endif
}


/*
 * get cm_to_env *cte from cm_to_env link chain and remove from the chain
 */
Private struct cm_to_env *
get_cte(ctep)
struct cm_to_env **ctep;
{
    struct cm_to_env *rp = NULL ;
    if(ctep){
	rp = *ctep ;
	if (rp != NULL){
	    *ctep = rp->ce_next ;
	} else {
	    *ctep = NULL ;
	}
	return rp ;
    } 
    return NULL;
}

Private (*opsw[MAX_ENV_OPCODE])();

Private iml_inst **
eval_it(s, cte, rrv)
iml_session_t *s ;
struct cm_to_env *cte;
iml_inst **rrv;
{
    struct cm_to_env *rp ;
    iml_inst *lp = 0;
    int op ; 

    for(rp = get_cte(&cte) ; rp ; rp = get_cte(&cte)) {
	op = rp->ce_operation ;
	if(op > MAX_ENV_OPCODE || op < 0 ){
#ifdef SVR4
            fprintf(stderr,dgettext("SUNW_XIM_HTT_MSG",
            "htt: Invalid opcode %d is specified from cm -- ignored\n"), op);
#else /* SVR4 */
            fprintf(stderr,"htt: Invalid opcode %d is specified from cm -- ignored\n", op);
#endif /* SVR4 */
	    continue ;
	}
	lp = NULL ;
	(*opsw[op])(s, &lp , &rp);
	s->If->m->iml_link_inst_tail(rrv, lp);
    }
    return(&lp->next); /* iml_inst lin last point */
}
Private int
cm_make_status_string(s,ws,s1,simple)
iml_session_t *s ; 
wchar_t *ws ; /* This sould be s->status_cache.ws */
unsigned char *s1 ;
int simple ;
{
    wchar_t	*list[MAXSTATUS];
    wchar_t	*name[MAXSTATUS];
    wchar_t	*value[MAXSTATUS];
    wchar_t	*string[MAXSTATUS];
    int         order[MAXSTATUS];
    int         i = 0, k = 0, j = 0;
    int         num = 0;
    wchar_t    *p ;
    char       *strtok();
    wchar_t    *my_wstok();
    wchar_t     s2[BUFSIZE];
    wchar_t	w[2];
    cmif_spec_t *if_spec = (cmif_spec_t *)s->If->if_spec ;
    
    if(simple){
	return mbstowcs(ws, (char *)s1, MAXSTATUS) ; /* assume s < MAXSTATUS */
    } else {    
	mbstowcs(s2, (char *)s1, BUFSIZE);
	mbstowcs(w, "(", 2);
	list[i] = (wchar_t *) my_wstok(s2, w);
	for (i = 1, num = 0;
	     ((list[i] =  my_wstok(NULL, w)) != NULL);
	     i++, num++);

	mbstowcs(w, ")", 2);
	for(i = 0; i <= num ; i++)
		my_wstok(list[i], w);
	
	mbstowcs(w, " ", 2);
	for (i = 0; i <= num; i++) {
	    name[i] =  my_wstok(list[i], w);
	    value[i] = my_wstok(NULL, w);
	}
	for (i = 0; i <= num; i++) {
	    if (!((name[i]) && (value[i])))	continue;
	    for (k = 0; k <= if_spec->NStatusInFile; k++) {
		if (((wscmp(name[i], if_spec->StatusString[k].name)) == 0) &&
		    (wscmp(value[i], if_spec->StatusString[k].value) == 0)) {
		    value[i] = if_spec->StatusString[k].value;
		    string[i] = if_spec->StatusString[k].string;
                    order[i] = if_spec->StatusString[k].order;
		    break;
		}
	    }
	}
	for (j = 0; j <= num; j++) {
	    for (i = 0; i <= if_spec->NStatus; i++) {
		if (if_spec->CurrentStatusString[i].name &&
		    (order[j] == i + 1)){
		    if_spec->CurrentStatusString[i].value = value[j];
		    if_spec->CurrentStatusString[i].string = string[j];
		    break;
		}
	    }
	}
	p = ws ;
	for (i = 0; i <= if_spec->NStatus; i++)
	  if (if_spec->CurrentStatusString[i].string) {
	      wscpy(p, if_spec->CurrentStatusString[i].string);
	      p += wslen(if_spec->CurrentStatusString[i].string);
	  }
	/*
	 * We don't check the status string length is shorter than 
	 * s->status_cache.size. This is sort of bug.
	 * but currently this buffer size is enough big for status string
	 */
	return p - ws ;
    }
}

wchar_t *my_wstok(s1, s2)
    wchar_t	*s1;
    wchar_t	*s2;
{
    wchar_t	*sp;
    wchar_t	*t1, *t2;
    wchar_t	*ret;
    static wchar_t	*ss = (wchar_t *)NULL;
    int		match;

    if(s1) {
	for(t1 = s1; *t1; t1++) {
		match = 0;
		for(t2 = s2; *t2; t2++) {
			if(*t1 == *t2) {
				match = 1;
				break;
			}
		}
		if(!match)
			break;
	}
        if(!t1) {
	        ss = (wchar_t *)NULL;
	        return (wchar_t *)NULL;
        }
	else {
		ss = sp = t1;
	}
    }
    else
	sp = ss;

    if( !ss && !s1 )
	return (wchar_t *)NULL;

    for(t1 = sp ; *t1; t1++) {
	match = 0;
	for(t2 = s2; *t2; t2++) {
		if(*t1 == *t2) {
			match = 1;
			break;
    		}
	}
	if(match) {
		*t1 = (wchar_t)NULL;
		ret = ss;
		ss = ++t1;
		return ret;
	}
    }
    
    ret = ss;
    ss = (wchar_t *)NULL;
    return ret;
}

Private unsigned char *
get_mlepath(s, path)
iml_session_t *s ;
char *path;
{
    char *openwinpath,*mlepath,*getenv();
    struct stat statbuf;    

    if ((mlepath = getenv("MLEPATH")) != (char *)0) {
	sprintf(path, "%s/%s/%s\0", mlepath, s->If->locale, MODE_DAT_PATH);
        if (stat(path, &statbuf) >= 0) {
            return (unsigned char *) path ;
	}
    }
    if ((openwinpath = getenv("OPENWINHOME")) != (char *)0) {
	sprintf(path, "%s/%s/%s/%s\0", openwinpath, OW_LOCALE_PATH,
			s->If->locale, MODE_DAT_PATH);
	if (stat(path, &statbuf) >= 0) {
		return (unsigned char *) path ;
	}
    }
    sprintf(path, "%s/%s/%s/%s\0", DEFAULT_OPENWINHOME, OW_LOCALE_PATH,
			s->If->locale, MODE_DAT_PATH);
    if (stat(path, &statbuf) >= 0) {
	return (unsigned char *) path ;
    }
    sprintf(path, "%s/%s/%s\0", DEFAULT_MLEPATH, s->If->locale, MODE_DAT_PATH);
    if (stat(path, &statbuf) >= 0) {
	return (unsigned char *) path ;
    }
    sprintf(path, "%s/%s/%s\0", DEFAULT_MLEPATH, s->If->locale, XXXCOMPAT_MODE_DAT_PATH);
    if (stat(path, &statbuf) >= 0) {
	return (unsigned char *) path ;
    }
    /*
     * Because this mode.dat file is JLE cm specific
     */
    sprintf(path, "%s/%s/%s\0", DEFAULT_MLEPATH, "ja", XXXCOMPAT_MODE_DAT_PATH);
    if (stat(path, &statbuf) >= 0) {
	return (unsigned char *) path ;
    }
    sprintf(path, "%s/%s/%s\0", DEFAULT_MLEPATH, "japanese", XXXCOMPAT_MODE_DAT_PATH);
    return (unsigned char *) path ;
}
Private void
init_status_string(s)
iml_session_t *s ;
{
    int		   file_fd;
    char           *name;
    char           *value;
    char           *p1, *p2;
    char           buf[BUFSIZE];
    wchar_t	   buf2[BUFSIZE];
    int j, k;
    cmif_spec_t    *if_spec = (cmif_spec_t *)s->If->if_spec ;
#ifndef MAXPATHLEN
#define MAXPATHLEN 255
#endif
    char mlepath[MAXPATHLEN];
    (void)get_mlepath(s, mlepath);
    if_spec->NStatusInFile = -1 ;
    file_fd = open((char *) mlepath, O_RDONLY);
    if (file_fd == -1) {	/* just in case, try again */
	sleep(1);
	file_fd = open((char *) mlepath, O_RDONLY);
    }
    if(file_fd) {
	ReadOneLine((char *)NULL, 0, file_fd);
	while ((ReadOneLine(buf, BUFSIZE, file_fd)) != 0) {
	    if (buf[0] == '#')
	      continue;
	    if_spec->NStatusInFile++;
	    p1 = buf;
	    p2 = (char *) index(p1, ':');
	    *p2 = '\0';
	    mbstowcs(if_spec->StatusString[if_spec->NStatusInFile].name,
		     p1, p2 - p1);
	    p1 = p2 + 1;
	    p2 = (char *) index(p1, ':');
	    *p2 = '\0';
	    mbstowcs(if_spec->StatusString[if_spec->NStatusInFile].value,
		     p1, p2 - p1);
	    p1 = p2 + 1;
	    p2 = (char *) index(p1, ':');
	    *p2 = '\0';
	    mbstowcs(if_spec->StatusString[if_spec->NStatusInFile].string,
		     p1, p2 - p1);
	    if_spec->StatusString[if_spec->NStatusInFile].order =
	      atoi(p2 + 1);
	}
	k = if_spec->StatusString[0].order - 1;
	if_spec->CurrentStatusString[k].name = if_spec->StatusString[0].name;
	if_spec->CurrentStatusString[k].value = if_spec->StatusString[0].value;
	if_spec->CurrentStatusString[k].string = if_spec->StatusString[0].string;
	
	for (j = 1, if_spec->NStatus = 0; j <= if_spec->NStatusInFile; j++) {
	    if ((!if_spec->CurrentStatusString[k].name) ||
		(!if_spec->StatusString[j].name))
	      continue;
	    if ((wscmp(if_spec->CurrentStatusString[k].name,
		       if_spec->StatusString[j].name))!= 0) {
		k = if_spec->StatusString[j].order - 1;
		if_spec->CurrentStatusString[k].name = if_spec->StatusString[j].name;
		if_spec->CurrentStatusString[k].value = if_spec->StatusString[j].value;
		if_spec->CurrentStatusString[k].string = if_spec->StatusString[j].string;
		if_spec->NStatus++;
	    }
	}
	close(file_fd);
    } else {
	/* assume using SIMPLE MODE */
    }
}

Private
ReadOneLine(buf, nb, fd)
char  *buf;
int   nb;
int   fd;
{
    int	   	  read_bytes;
    char	  *readpointer, *endofline;
    static char	  ibuf[BUFSIZE];
    static int	  index = 0;
    int		  prevbytes = 0;
    int		  returnbytes;

    if(nb == 0){
	index = 0;
	return 0;
    }
	
    readpointer = ibuf + index;
    if(index == 0) {
    	read_bytes = read(fd, ibuf, nb-1);
	if(read_bytes <= 0)
		return NULL;
    	ibuf[read_bytes] = 0;
    }

    if(!(endofline = (char *) strchr(readpointer, (int)'\n'))) {
	prevbytes = BUFSIZE-index-1;
	bcopy(readpointer, buf, prevbytes);
	read_bytes = read(fd, ibuf, nb-1);
	if(read_bytes <= 0)
		return NULL;
	ibuf[read_bytes] = 0;
	if(!(endofline = (char *) strchr(ibuf , (int)'\n')))
		return NULL;
	readpointer = ibuf;
    }

    returnbytes = endofline - readpointer + 1;
    bcopy(readpointer, buf + prevbytes, returnbytes);
    buf[prevbytes + returnbytes] = 0;
    index = endofline - ibuf + 1;
     
    return (returnbytes);
}

Private void
eval_cm_to_env_packet(s, cte)
iml_session_t *s ;
struct cm_to_env *cte;
{
    iml_inst *rv = NULL ;
    eval_it(s, cte, &rv);
    if(rv = s->If->m->iml_execute(s, &rv)){
	cm_eval_results(s, rv);
    }
}
Private void
cm_destruct_session(s)
iml_session_t *s ;
{
    s->If->m->iml_destruct_session(s, destruct_session);

}
Private int
destruct_session(s)
iml_session_t *s ;
{
    struct cm_to_env *ctep = (struct cm_to_env *)
	cm_close(s->language_engine, s->session_id);
    eval_cm_to_env_packet(s, ctep);
    return 0;
}

Private void
cm_reset(s, rrv)
iml_session_t *s ;
iml_inst **rrv ;
{
    iml_inst *lp ;
    lp = s->If->m->iml_make_nop_inst(s);
    s->If->m->iml_link_inst_tail(rrv, lp);
}
/*
 * Commit intermediate text by sending CM_RESET to Language engine.
 * This function is called from XmbReset();
 * IMLogic(,,IML_COMMIT,) and then iml_send_commit().
 * Assignment of cm_send_commit() to function entity of
 * iml_send_commit() is done in cm_configure().
 *
 */
Private void
cm_send_commit(s)
iml_session_t *s;
{
    struct cm_to_env *ctep;
    static struct env_to_cm etc;

    if(s->remainder){
	if(s->remainder = s->If->m->iml_execute(s, &s->remainder)){
	    cm_eval_results(s, s->remainder);
	}
        s->remainder = NULL;
        return;
    }
    etc.ec_operation = CM_RESET;
    etc.ec_next = NULL ;
    s->public_status &= ~IMLSTATUS_Henkan_Mode ;
    ctep = cm_put(s->language_engine, s->session_id, &etc);
    eval_cm_to_env_packet(s, ctep);
}

Private void
cm_set_choice(s, i)
iml_session_t *s ;
int i ; /* index */
{
    Private struct cm_to_env cte ;
    Private struct cm_to_env *ctep = &cte ;
    Private struct env_to_cm etc ;
    
    etc.ec_operation = CM_SELECT ;
    etc.ec_select = i ;
    etc.ec_next = NULL ;
    ctep = cm_put(s->language_engine, s->session_id, &etc);
    eval_cm_to_env_packet(s, ctep);
    /* BugId : 4212962. Do not free memory here
       _iml_delete2(s); */
}

Private void
cm_select_unknown_control(s)
iml_session_t *s ;
{
    Private struct cm_to_env cte ;
    Private struct cm_to_env *ctep = &cte ;
    Private struct env_to_cm etc ;

    if(translate_keysym_to_cm_keycode(s, s->keysym, &etc.ec_key, 1)){
        iml_inst **rrv ;
        etc.ec_operation = CM_SIMPLE_EVENT;
        etc.ec_next = NULL ;
        ctep = cm_put(s->language_engine, s->session_id, &etc);
        eval_cm_to_env_packet(s, ctep);
    }
}

Private void
cm_attr2feedback(p, q, len, e)
XIMFeedback *p ;
unsigned char *q ; 
int len ;
char *e ;
{
    register i,j,l;
    register char r ;
    
#ifdef	DEBUG
    _iml_xdmem(q,len*sizeof(wchar_t));
#endif
    
    for(i=0,j=0;j<len;i+=l,j++){
#ifdef NEXT_GENERATION_OF_JLE_ATTRIBUTE_COMPATIBLE
	p[i] = q[j] ;
#else
	l = mblen(e+i,4);
	r = q[i];
	p[j]=0;

	if(r & ATTR_REVERSE)
	  p[j] |= XIMReverse ;
	else
	  p[j] |= XIMUnderline ; /* because of cm bug */

#endif
    }
    
#ifdef	DEBUG
    _iml_xdmem(p,len*sizeof(XIMFeedback)) ;
#endif
}
Private void
cm_opsw_init(void)
{
    register i ;
    
    if(opsw[ENV_NOP]) return;

    for(i = 0 ; i < MAX_ENV_OPCODE ; i++){
	opsw[i] = env_nop ;
    }
    opsw[ENV_NOP] = env_nop ;
    opsw[ENV_COMMIT] = env_commit ;
    opsw[ENV_SET_CURRENT_REGION] = env_set_current_region ;
    opsw[ENV_CM_OFF] = env_cm_off ;
    opsw[ENV_SETKEY_CM_ON] = env_setkey_cm_on ;
    opsw[ENV_INTERM_RESET] = env_interm_reset ;
    opsw[ENV_INTERM] = env_interm ;
    opsw[ENV_M_INTERACTIVE] = env_m_interactive ;
    opsw[ENV_SELECT_RESET] = env_select_reset ;
    opsw[ENV_SELECT] = env_select ;
    opsw[ENV_SELECT_END] = env_select_end ;
    opsw[ENV_SELECT_NEXT] = env_select_next ;
    opsw[ENV_SELECT_PREV] = env_select_prev ;
    opsw[ENV_SELECT_COMMIT] = env_select_commit ;
    opsw[ENV_SELECT_TEST] = env_nop ;
    opsw[ENV_SET_SIMPLE_MODE] =  env_set_smode ;
    opsw[ENV_SET_MODE] = env_set_mode ;
    opsw[ENV_MISC_RESET] = env_misc_reset ;
    opsw[ENV_M_LABEL] = env_m_label ;
    opsw[ENV_M_MESS] = env_nop ;

}
Private Bool
translate_keysym_to_cm_keycode(s, keysym, cm_keycode, l)
iml_session_t *s ;
KeySym keysym ;
unsigned long *cm_keycode;
int	l;
{
#define functionKey(keysym,s) 
    switch(keysym){
      case XK_Shift_L:
      case XK_Shift_R:
      case XK_Control_L:
      case XK_Control_R:
      case XK_Caps_Lock:
      case XK_Shift_Lock:
      case XK_Meta_L:
      case XK_Meta_R:
      case XK_Alt_L:
      case XK_Alt_R:
      case XK_Super_L:
      case XK_Super_R:
      case XK_Hyper_L:
      case XK_Hyper_R:
	/*
	 * Only modifier key is coming..
	 */
	return False;
	/*
	 * Function Key Operations
	 */
      case XK_Multi_key: /* 0xFF20  Multi-key character compose */
	functionKey( XK_Multi_key, "XK_Multi_key");
	break;
      case XK_Kanji: /* 0xFF21	Kanji, Kanji convert */
	functionKey( XK_Kanji, "XK_Kanji");
	*cm_keycode = 0x0002 | CM_BF_START ;
	goto VALID;
	break;
      case XK_Muhenkan: /* 0xFF22   Cancel Conversion */
	functionKey( XK_Muhenkan, "XK_Muhenkan");
	break;
      case SunXK_Hangul: /* 0xFF31  Start/Stop Conversion */
      case XK_Henkan_Mode: /* 0xFF23  Start/Stop Conversion */
	functionKey( XK_Henkan_Mode, "XK_Henkan_Mode");
	*cm_keycode = 0x0003 | CM_BF_START ;
	goto VALID;
	break;
      case XK_Romaji: /* 0xFF24   to Romaji */
	functionKey( XK_Romaji, "XK_Romaji");
	break;
      case XK_Hiragana: /* 0xFF25   to Hiragana */
	functionKey( XK_Hiragana, "XK_Hiragana");
	break;
      case XK_Katakana: /* 0xFF26   to Katakana */
	functionKey( XK_Katakana, "XK_Katakana");
	break;
      case XK_Hiragana_Katakana: /* 0xFF27  Hiragana/Katakana toggle */
	functionKey( XK_Hiragana_Katakana, "XK_Hiragana_Katakana");
	break;
      case XK_Zenkaku: /* 0xFF28   to Zenkaku */
	functionKey( XK_Zenkaku, "XK_Zenkaku");
	break;
      case XK_Hankaku: /* 0xFF29   to Hankaku */
	functionKey( XK_Hankaku, "XK_Hankaku");
	break;
      case XK_Zenkaku_Hankaku: /* 0xFF2A   Zenkaku/Hankaku toggle */
	functionKey( XK_Zenkaku_Hankaku, "XK_Zenkaku_Hankaku");
	break;
      case XK_Touroku: /* 0xFF2B   Add to Dictionary */
	functionKey( XK_Touroku, "XK_Touroku");
	break;
      case XK_Massyo: /* 0xFF2C   Delete from Dictionary */
	functionKey( XK_Massyo, "XK_Massyo");
	break;
      case XK_Kana_Lock: /* 0xFF2D  Kana Lock */
	functionKey( XK_Kana_Lock, "XK_Kana_Lock");
	break;
      case XK_Kana_Shift: /* 0xFF2E  Kana Shift */
	functionKey( XK_Kana_Shift, "XK_Kana_Shift");
	break;
      case XK_Eisu_Shift: /* 0xFF2F  Alphanumeric Shift */
	functionKey( XK_Eisu_Shift, "XK_Eisu_Shift");
	break;
      case XK_Eisu_toggle: /* 0xFF30  Alphanumeric toggle */
	functionKey( XK_Eisu_toggle, "XK_Eisu_toggle");
	break;
      case XK_Home: /* 	0xFF50 */
	functionKey( XK_Home, "XK_Home");
	break;
      case XK_Left: /* 	0xFF51 Move left, left arrow */
	functionKey( XK_Left, "XK_Left");
	break;
      case XK_Up: /* 	0xFF52	/* Move up, up arrow */
	functionKey( XK_Up, "XK_Up");
	break;
      case XK_Right: /* 0xFF53	/* Move right, right arrow */
	functionKey( XK_Right, "XK_Right");
	break;
      case XK_Down: /* 	0xFF54	/* Move down, down arrow */
	functionKey( XK_Down, "XK_Down");
	break;
      case XK_Prior: /* 0xFF55	/* Prior, previous */
	functionKey( XK_Prior, "XK_Prior");
	break;
      case XK_Next: /* 	0xFF56	/* Next */
	functionKey( XK_Next, "XK_Next");
	break;
      case XK_End: /* 	0xFF57	/* EOL */
	functionKey( XK_End, "XK_End");
	break;
      case XK_Begin: /* 0xFF58	/* BOL */
	functionKey( XK_Begin, "XK_Begin");
	break;
      case XK_Select: /* 0xFF60	 Select, mark */
	functionKey( XK_Select, "XK_Select");
	break;
      case XK_Print: /* 0xFF61 */
	functionKey( XK_Print, "XK_Print");
	break;
      case XK_Execute: /* 0xFF62  Execute, run, do */
	functionKey( XK_Execute, "XK_Execute");
	*cm_keycode = 0xb001 | CM_BF_START ;
	goto VALID;
	break;
      case XK_Insert: /* 0xFF63	 Insert, insert here */
	functionKey( XK_Insert, "XK_Insert");
	break;
      case XK_Undo: /* 	0xFF65	Undo, oops */
	functionKey( XK_Undo, "XK_Undo");
	break;
      case XK_Redo: /* 	0xFF66	redo, again */
	functionKey( XK_Redo, "XK_Redo");
	break;
      case XK_Menu: /* 	0xFF67 */
	functionKey( XK_Menu, "XK_Menu");
	break;
      case XK_Find: /* 	0xFF68	 Find, search */
	functionKey( XK_Find, "XK_Find");
	break;
      case XK_Cancel: /* 0xFF69	Cancel, stop, abort, exit */
	functionKey( XK_Cancel, "XK_Cancel");
	break;
      case XK_Help: /* 	0xFF6A	Help, ? */
	functionKey( XK_Help, "XK_Help");
	break;
      case XK_Break: /* 0xFF6B */
	functionKey( XK_Break, "XK_Break");
	break;
      case XK_Mode_switch: /* 0xFF7E Character set switch */
	/* case XK_script_switch: 0xFF7E Alias for mode_switch */
	functionKey( XK_Mode_switch, "XK_Mode_switch");
	/*
	 * something wrong in cm if there is any problem here.
	 */
    	*cm_keycode = 0xb004 | CM_BF_START ;
    	goto VALID;
	break;
      case SunXK_Hangul_Hanja: /* 0xFF34 HangleCharacter set switch */
	functionKey( SunXK_Hangul_Hanja, "SunXK_Hangui_Hanja");
    	*cm_keycode = 0xb004 | CM_BF_START ;
    	goto VALID;
	break;
      case XK_Num_Lock: /* 0xFF7F */
	functionKey( XK_Num_Lock, "XK_Num_Lock");
	*cm_keycode = 0xb004 | CM_RF_START ;
	break;
      case XK_KP_Space: /* 0xFF80	 space */
	functionKey( XK_KP_Space, "XK_KP_Space");
	break;
      case XK_KP_Tab: /* 0xFF89 */
	functionKey( XK_KP_Tab, "XK_KP_Tab");
	break;
      case XK_KP_Enter: /* 0xFF8D enter */
	functionKey( XK_KP_Enter, "XK_KP_Enter");
	*cm_keycode = '\n';
	goto VALID;
	break;
      case XK_KP_F1: /* 0xFF91	PF1, KP_A, ... */
	functionKey( XK_KP_F1, "XK_KP_F1");
	break;
      case XK_KP_F2: /* 0xFF92 */
	functionKey( XK_KP_F2, "XK_KP_F2");
	break;
      case XK_KP_F3: /* 0xFF93 */
	functionKey( XK_KP_F3, "XK_KP_F3");
	break;
      case XK_KP_F4: /* 0xFF94 */
	functionKey( XK_KP_F4, "XK_KP_F4");
	break;
      case XK_KP_Equal: /* 0xFFBD  equals */
	functionKey( XK_KP_Equal, "XK_KP_Equal");
	*cm_keycode = '=';
	goto VALID;
	break;
      case XK_KP_Multiply: /* 0xFFAA */
	functionKey( XK_KP_Multiply, "XK_KP_Multiply");
	*cm_keycode = '*';
	goto VALID;
	break;
      case XK_KP_Add: /* 0xFFAB */
	functionKey( XK_KP_Add, "XK_KP_Add");
	*cm_keycode = '+';
	goto VALID;
	break;
      case XK_KP_Separator: /* 0xFFAC separator, often comma */
	functionKey( XK_KP_Separator, "XK_KP_Separator");
	*cm_keycode = ',';
	goto VALID;
	break;
      case XK_KP_Subtract: /* 0xFFAD */
	functionKey( XK_KP_Subtract, "XK_KP_Subtract");
	*cm_keycode = '-';
	goto VALID;
	break;
      case XK_KP_Decimal: /* 0xFFAE */
	functionKey( XK_KP_Decimal, "XK_KP_Decimal");
	*cm_keycode = '.';
	goto VALID;
	break;
      case XK_KP_Divide: /* 0xFFAF */
	functionKey( XK_KP_Divide, "XK_KP_Divide");
	*cm_keycode = '/';
	goto VALID;
	break;
      case XK_KP_0: /* 	0xFFB0 */
	functionKey( XK_KP_0, "XK_KP_0");
	*cm_keycode = '0';
	goto VALID;
	break;
      case XK_KP_1: /* 	0xFFB1 */
	functionKey( XK_KP_1, "XK_KP_1");
	*cm_keycode = '1';
	goto VALID;
	break;
      case XK_KP_2: /* 	0xFFB2 */
	functionKey( XK_KP_2, "XK_KP_2");
	*cm_keycode = '2';
	goto VALID;
	break;
      case XK_KP_3: /* 	0xFFB3 */
	functionKey( XK_KP_3, "XK_KP_3");
	*cm_keycode = '3';
	goto VALID;
	break;
      case XK_KP_4: /* 	0xFFB4 */
	functionKey( XK_KP_4, "XK_KP_4");
	*cm_keycode = '4';
	goto VALID;
	break;
      case XK_KP_5: /* 	0xFFB5 */
	functionKey( XK_KP_5, "XK_KP_5");
	*cm_keycode = '5';
	goto VALID;
	break;
      case XK_KP_6: /* 	0xFFB6 */
	functionKey( XK_KP_6, "XK_KP_6");
	*cm_keycode = '6';
	goto VALID;
	break;
      case XK_KP_7: /* 	0xFFB7 */
	functionKey( XK_KP_7, "XK_KP_7");
	*cm_keycode = '7';
	goto VALID;
	break;
      case XK_KP_8: /* 	0xFFB8 */
	functionKey( XK_KP_8, "XK_KP_8");
	*cm_keycode = '8';
	goto VALID;
	break;
      case XK_KP_9: /*  0xFFB9 */
	functionKey( XK_KP_9, "XK_KP_9");
	*cm_keycode = '9';
	goto VALID;
	break;
      case XK_F1:
	functionKey(XK_F1,"XK_F1");
	*cm_keycode = 0x1 | CM_TF_START ;
	goto VALID;
	break;
      case XK_F2:
	functionKey(XK_F2,"XK_F2");
	*cm_keycode = 0x2 | CM_TF_START ;
	goto VALID;
	break;
      case XK_F3:
	functionKey(XK_F3,"XK_F3");
	*cm_keycode = 0x3 | CM_TF_START ;
	goto VALID;
	break;
      case XK_F4:
	functionKey(XK_F4,"XK_F4");
	*cm_keycode = 0x4 | CM_TF_START ;
	goto VALID;
	break;
      case XK_F5:
	functionKey(XK_F5,"XK_F5");
	*cm_keycode = 0x5 | CM_TF_START ;
	goto VALID;
	break;
      case XK_F6:
	functionKey(XK_F6,"XK_F6");
	*cm_keycode = 0x6 | CM_TF_START ;
	goto VALID;
	break;
      case XK_F7:
	functionKey(XK_F7,"XK_F7");
	*cm_keycode = 0x7 | CM_TF_START ;
	goto VALID;
	break;
      case XK_F8:
	functionKey(XK_F8,"XK_F8");
	*cm_keycode = 0x8 | CM_TF_START ;
	goto VALID;
	break;
      case XK_F9:
	functionKey(XK_F9,"XK_F9");
	*cm_keycode = 0x9 | CM_TF_START ;
	goto VALID;
	break;
      case XK_F10:
	functionKey(XK_F10,"XK_F10");
	*cm_keycode = 0xa | CM_TF_START ;
	goto VALID;
	break;
#if XlibSpecificationRelease != 5
#ifdef OWNV2
      case XK_SunF36: 
#endif /* sun */
#endif /* XlibSpecificationRelease */
      case SunXK_F36: /* Labeled F11 */
	*cm_keycode = 0xb | CM_TF_START ;
	goto VALID;
	break;
#if XlibSpecificationRelease != 5
#ifdef OWNV2
      case XK_SunF37: 
#endif /* sun */
#endif /* XlibSpecificationRelease */
      case SunXK_F37: /* Labeled F12 */
	functionKey(XK_F12,"XK_F12");
	*cm_keycode = 0xc | CM_TF_START ;
	goto VALID;
	break;
/*
 * don't trap Left/right function key
 */
#define break /**/
      case XK_F11: /* XK_L1 */
	functionKey(XK_F11,"XK_F11");
	break;
      case XK_F12: /* XK_L2 */
	functionKey(XK_F12,"XK_F12");
	break;
      case XK_F13: /* XK_L3 */
	functionKey(XK_F13,"XK_F13");
	break;
      case XK_F14: /* XK_L4 */
	functionKey(XK_F14,"XK_F14");
	break;
      case XK_F15: /* XK_L5 */
	functionKey(XK_F15,"XK_F15");
	break;
      case XK_F16: /* XK_L6 */
	functionKey(XK_F16,"XK_F16");
	break;
      case XK_F17: /* XK_L7 */
	functionKey(XK_F17,"XK_F17");
	break;
      case XK_F18: /* XK_L8 */
	functionKey(XK_F18,"XK_F18");
	break;
      case XK_F19: /* XK_L9 */
	functionKey(XK_F19,"XK_F19");
	break;
      case XK_F20: /* XK_L10 */
	functionKey(XK_F20,"XK_F20");
	break;
      case XK_F21: /* XK_R1 */
	functionKey(XK_F21,"XK_F21");
	break;
      case XK_F22: /* XK_R2 */
	functionKey(XK_F22,"XK_F22");
	break;
      case XK_F23: /* XK_R3 */
	functionKey(XK_F23,"XK_F23");
	break;
      case XK_F24: /* XK_R4 */
	functionKey(XK_F24,"XK_F24");
	break;
      case XK_F25: /* XK_R5 */
	functionKey(XK_F25,"XK_F25");
	break;
      case XK_F26: /* XK_R6 */
	functionKey(XK_F26,"XK_F26");
	break;
      case XK_F27: /* XK_R7 */
	functionKey(XK_F27,"XK_F27");
	break;
      case XK_F28: /* XK_R8 */
	functionKey(XK_F28,"XK_F28");
	break;
      case XK_F29: /* XK_R9 */
	functionKey(XK_F29,"XK_F29");
	break;
      case XK_F30: /* XK_R10 */
	functionKey(XK_F30,"XK_F30");
	break;
      case XK_F31: /* XK_R11 */
	functionKey(XK_F31,"XK_F31");
	break;
      case XK_F32: /* XK_R12 */
	functionKey(XK_F32,"XK_F32");
	break;
      case XK_R13: /* XK_F33 */
	functionKey(XK_R13,"XK_R13");
	break;
      case XK_F34: /* XK_R14 */
	functionKey(XK_F34,"XK_F34");
	break;
      case XK_F35: /* XK_R15 */
	functionKey(XK_F35,"XK_F35");
	break;
	return False ;
#undef break
      default:	
	if(l)
	    *cm_keycode = s->XLookupBuf[0]; /* How badly cm is designed! */
VALID:
	return True ;
    }
    return False ;
}
