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

*/

/*
 * $Id: iiimpAux.c,v 1.1.1.1.2.4 2001/11/05 19:40:34 tajima Exp $
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <sys/param.h>
#ifdef HAVE_SYNCN_H
#include <synch.h>
#endif

#include <X11/Xmd.h>
#include <X11/Xlib.h>

#include "Xlcint.h"

#include "iiimpIM.h"
#include "iiimpAux.h"
#include "iiimpAuxP.h"
#include "guiIMPre.h"
#include "iiimpConv.h"

#include "XimpIm.h"

#include "trace_message.h"


#define AUX_BASE_DIR		IIIMLEDIR

#if defined(__sparcv9)
#define SPARCV9_DIR		"sparcv9/"
#define SPARCV9_DIR_LEN		(8)
#endif /* __sparcv9 */

typedef struct _aux_entry {
    int created;
    aux_dir_t dir;
    unsigned int if_version;
} aux_entry_t;

 
typedef struct _aux_handle {
    aux_name_t aux_name;
    char *file_name;
    void *handle;
    aux_entry_t *ae;
    int ae_num; 
    struct _aux_handle *next;
} aux_handle_t;


struct _aux_im_data {
    int im_id;
    int ic_id;
    aux_entry_t *ae;
    void *data;
    aux_im_data_t *next;
};


typedef struct _aux_ic_info {
    int im_id;
    int ic_id;
    XicCommon ic_common;
    struct _aux_ic_info *next;
} aux_ic_info_t;

#define AUX_DIR_SYMBOL		"aux_dir"
#define AUX_CONF_MAGIC		"# IIIM X auxiliary"

#define AUX_INFO_SYMBOL		"aux_info"


#define IS_SPACE(len, ptr)	((0 < (len)) &&				\
				 (('\t' == *(p)) || (' ' == *(p))))
#define IS_EOL(len, ptr)	(((len) <= 0) || ('\n' == *(p)))
#define IS_NOT_EOL(len, ptr)	((0 < (len)) && ('\n' != *(p)))

#define IS_PREEDIT_POSITION(ic)						 \
			(XIMP_CHK_PRESPOTLMASK(ic) &&			 \
			 (!((ic)->core.input_style & XIMPreeditArea)) && \
			 ((ic->core.input_style & XIMPreeditPosition) || \
			 (ic->core.input_style & XIMPreeditCallbacks)) )

/*
 * internal method
 */
static aux_handle_t*
aux_load(
    char * aux_file_name
);
static aux_handle_t*
aux_conf_load(
    char * aux_file_name
);
static aux_handle_t*
aux_so_load(
    char * aux_file_name
);
static aux_t*
aux_get(
    XicCommon ic,
    IIIMCF_event ev,
    const IIIMP_card16 *aux_name
);
static aux_entry_t*
aux_entry_get(
    const IIIMP_card16 *name
);
static aux_im_data_t*
aux_im_get(
    XicCommon, int
);
static int 
aux_string_length(
    const IIIMP_card16 *str
);

/* faked "COMPOSED":-P structure.  */
typedef struct {
    int len;
    aux_t *aux;
    IIIMCF_event ev;
    aux_data_t *pad;
} AUXComposed;

/*
 * X auxiliary object service method
 */
static void		service_aux_setvalue(aux_t *,
					     const unsigned char *, int);
static void		service_aux_getvalue(aux_t *,
					     const unsigned char *, int);
static int		service_im_id(aux_t *);
static int		service_ic_id(aux_t *);
static void		service_data_set(aux_t *, int, void *);
static void *		service_data_get(aux_t *, int);
static Display *	service_display(aux_t *);
static Window		service_window(aux_t *);
static XPoint *		service_point(aux_t *, XPoint *);
static XPoint *		service_point_caret(aux_t *, XPoint *);
static size_t		service_utf16_mb(const char **, size_t *,
					 char **, size_t *);
static size_t		service_mb_utf16(const char **, size_t *,
					 char **, size_t *);
static unsigned char *	service_compose(const aux_data_t *, int *);
static int		service_compose_size(aux_data_type_t,
					     const unsigned char *);
static aux_data_t *	service_decompose(aux_data_type_t,
					  const unsigned char *);
static void		service_decompose_free(aux_data_t *);
static void		service_register_X_filter(Display *, Window, int, int,
						  Bool (* filter)(Display *,
								  Window,
								  XEvent *,
								  XPointer),
						  XPointer);
static void		service_unregister_X_filter(Display *, Window,
						    Bool (* filter)(Display *,
								    Window,
								    XEvent *,
								    XPointer),
						    XPointer);
static Bool		service_server(aux_t *);
static Window		service_client_window(aux_t *);
static Window		service_focus_window(aux_t *);
static int		service_screen_number(aux_t *);
static int		service_point_screen(aux_t *, XPoint *);
static int		service_point_caret_screen(aux_t *, XPoint *);
static Bool		service_get_conversion_mode(aux_t*);
static void		service_set_conversion_mode(aux_t*, int);
static aux_t *		service_aux_get_from_id(int im_id, int ic_id,
						CARD16 *aux_name,
						int aux_name_length);

static aux_service_t aux_service = {
	service_aux_setvalue,
	service_im_id,
	service_ic_id,
	service_data_set,
	service_data_get,
	service_display,
	service_window,
	service_point,
	service_point_caret,
	service_utf16_mb,
	service_mb_utf16,
	service_compose,
	service_compose_size,
	service_decompose,
	service_decompose_free,
	service_register_X_filter,
	service_unregister_X_filter,
	service_server,
	service_client_window,
	service_focus_window,
	service_screen_number,
	service_point_screen,
	service_point_caret_screen,
	service_get_conversion_mode,
	service_set_conversion_mode,
	service_aux_getvalue,
	service_aux_get_from_id
};


#if defined(ENABLE_TRACE)
static const char *	AuxChangeReasonName[] = {
	"NONE",
	"AUX_DOWNLOAD",
	"AUX_START",
	"AUX_DRAW",
	"AUX_DONE",
	"AUX_SWITCHED",
	"AUX_DESTROY"
};
#endif /* ENABLE_TRACE */


static aux_handle_t *	aux_handle;
static aux_ic_info_t *	aux_ic_info = NULL;

/*
 * object downloading
 */
void
IIimpAuxDownload(
    IIIMCF_downloaded_object obj
)
{
    char *aux_file_name;
    char *aux_file_name_buf;
    int	aux_file_name_len;
    char file_name[MAXPATHLEN];
    char *dir_name;
    int	dir_name_len;
    Bool useUnicode = False; /* default */
    IIIMF_status st;
    const IIIMP_card16 *u16filename;

    TRACE_MESSAGE('A', ("iiimpAux: download\n"));

    aux_file_name = NULL;
    aux_file_name_buf = NULL;

    st = iiimcf_get_downloaded_object_filename(obj, &u16filename);
    if (st != IIIMF_STATUS_SUCCESS) return;
    st = IIimpUTF16ToString(u16filename, useUnicode, &aux_file_name);
    if (st != IIIMF_STATUS_SUCCESS) return;
    aux_file_name_buf = aux_file_name;

#if defined(IIIM_AUX_SO_PATH_ENV)
    p = getenv(IIIM_AUX_SO_PATH_ENV);

    if (NULL != p) {
	aux_file_name = p;
    }

    if (NULL == aux_file_name) {
	aux_file_name = "./aux.so";
    }
#endif /* IIIM_AUX_SO_PATH_ENV */

    if (NULL == aux_file_name) {
	return;
    }

    aux_file_name_len = strlen(aux_file_name);

    /*
     * may not start with "/"
     * may not start with "../"
     * may not contain "/../"
     * may not end with "/"
     * may not end with "/."
     * may not end with "/.."
     * may not be ".."
     */

    if (((1 <= aux_file_name_len) &&
	 ('/' == *(aux_file_name + 0))) ||
	((3 <= aux_file_name_len) &&
	 ('.' == *(aux_file_name + 0)) &&
	 ('.' == *(aux_file_name + 1)) &&
	 ('/' == *(aux_file_name + 2))) ||
	(NULL != strstr(aux_file_name, "/../")) ||
	((1 <= aux_file_name_len) &&
	 ('/' == *(aux_file_name + aux_file_name_len - 1))) ||
	((2 <= aux_file_name_len) &&
	 ('/' == *(aux_file_name + aux_file_name_len - 2)) &&
	 ('.' == *(aux_file_name + aux_file_name_len - 1))) ||
	((3 <= aux_file_name_len) &&
	 ('/' == *(aux_file_name + aux_file_name_len - 3)) &&
	 ('.' == *(aux_file_name + aux_file_name_len - 2)) &&
	 ('.' == *(aux_file_name + aux_file_name_len - 1))) ||
	((2 == aux_file_name_len) &&
	 ('.' == *(aux_file_name + 0)) &&
	 ('.' == *(aux_file_name + 1)))) {

	Xfree (aux_file_name_buf);
	return;
    }

    /*
     * eliminate leading "./"
     */
    if ((2 <= aux_file_name_len) &&
	('.' == *(aux_file_name + 0)) &&
	('/' == *(aux_file_name + 1))) {
	aux_file_name += 2;
	aux_file_name_len -= 2;
    }

    dir_name = AUX_BASE_DIR;

#if defined(IIIM_AUX_SO_BASE_ENV)
    {
	char *	p;
	if (NULL != (p = getenv(IIIM_AUX_SO_BASE_ENV))) {
	    dir_name = p;
	}
    }
#endif /* IIIM_AUX_SO_BASE_ENV */

    dir_name_len = strlen(dir_name);

#if defined(__sparcv9)
    if (MAXPATHLEN <
	(dir_name_len + 1 + aux_file_name_len + SPARCV9_DIR_LEN + 1)) {
	Xfree (aux_file_name_buf);
	return;
    }
    p = strrchr(aux_file_name, '/');
    if (NULL == p) {
	p = aux_file_name;
    } else {
	p += 1;
    }
    memcpy(file_name, dir_name, dir_name_len);
    len = (p - aux_file_name);
    if (dir_name_len > 0 && dir_name[dir_name_len-1] == '/') {
	memcpy(file_name + dir_name_len, aux_file_name, len);
	len += dir_name_len;
    } else {
	file_name[dir_name_len] = '/';
	memcpy(file_name + dir_name_len + 1, aux_file_name, len);
	len += (dir_name_len + 1);
    }
    memcpy(file_name + len, SPARCV9_DIR, SPARCV9_DIR_LEN);
    len += SPARCV9_DIR_LEN;
    memcpy(file_name + len, p, aux_file_name_len - (p - aux_file_name) + 1);

#else /* !__sparcv9 */

    if (MAXPATHLEN <= (dir_name_len + 1 + aux_file_name_len + 1)) {
	Xfree (aux_file_name_buf);
	return;
    }

    memcpy(file_name, dir_name, dir_name_len + 1);
    if (dir_name_len > 0 && dir_name[dir_name_len-1] == '/') {
	memcpy(file_name + dir_name_len,
	       aux_file_name, aux_file_name_len + 1);
    } else {
	file_name[dir_name_len] = '/';
	memcpy(file_name + dir_name_len + 1,
	       aux_file_name, aux_file_name_len + 1);
    }
#endif /* !__sparcv9 */

    (void)aux_load(file_name);

    Xfree (aux_file_name_buf);

    return;
}

static AUXComposed*
create_composed_from_event(
    aux_t *aux,
    IIIMCF_event ev
)
{
    unsigned char *p;
    AUXComposed *pac;
    IIIMF_status st;
    const IIIMP_card16 *aux_name;
    IIIMP_card32 class_idx;
    int num_intvals;
    const IIIMP_card32 *pintvals;
    int num_strvals;
    const IIIMP_card16 **pstrs;
    aux_data_t *pad;

    int i, n;
    int aux_data_t_n;
    int aux_name_len, aux_name_n;
    int integer_list_n = 0, string_list_n = 0;
    int string_n = 0;
    int *pstring_len;

#define ROUNDUP(n) ((n + sizeof(int) - 1) / sizeof(int) * sizeof(int))

    st = iiimcf_get_aux_event_value(ev, &aux_name, &class_idx,
				    &num_intvals, &pintvals,
				    &num_strvals, &pstrs);
    if (st != IIIMF_STATUS_SUCCESS) return NULL;

    /* first of all, caliculate size. */
    n = ROUNDUP(sizeof(AUXComposed));
    aux_data_t_n = n;
    n += sizeof(aux_data_t);
    aux_name_n = n = ROUNDUP(n);
    aux_name_len = aux_string_length(aux_name);
    n += (aux_name_len + 1) * sizeof(IIIMP_card16);
    if (num_intvals > 0) {
	integer_list_n = n = ROUNDUP(n);
	n += num_intvals * sizeof(int);
    }
    pstring_len = NULL;
    if (num_strvals > 0) {
	pstring_len = (int*) malloc(sizeof(int) * num_strvals);
	if (!pstring_len) return NULL;
	string_list_n = n = ROUNDUP(n);
	n += num_strvals * sizeof(aux_string_t);
	string_n = n = ROUNDUP(n);
	for (i = 0; i < num_strvals; i++) {
	    pstring_len[i] = aux_string_length(pstrs[i]);
	    n += (pstring_len[i] + 1) * sizeof(IIIMP_card16);
	}
    }
    p = (unsigned char*) malloc(n);
    if (!p) {
	if (pstring_len) free(pstring_len);
	return NULL;
    }
    memset(p, 0, n);

    pac = (AUXComposed*) p;
    pac->len = n;
    pac->ev = ev;
    pad = (aux_data_t*)(p + aux_data_t_n);
    pac->pad = pad;

    if (aux) {
	pac->aux = aux;
	pad->im = aux->im->im_id;
	pad->ic = aux->im->ic_id;
    }

    pad->aux_index = class_idx;
    pad->aux_name = p + aux_name_n;
    memcpy(pad->aux_name, aux_name, (aux_name_len + 1) * sizeof(IIIMP_card16));
    pad->aux_name_length = aux_name_len * sizeof(IIIMP_card16);

    pad->integer_count = num_intvals;
    if (num_intvals > 0) {
	pad->integer_list = (int*)(p + integer_list_n);
	for (i = 0; i < num_intvals; i++) {
	    pad->integer_list[i] = pintvals[i];
	}
    }
    pad->string_count = num_strvals;
    pad->string_ptr = p;
    if (num_strvals > 0) {
	aux_string_t *pas;

	pad->string_list = pas = (aux_string_t*)(p + string_list_n);
	p += string_n;
	for (i = 0; i < num_strvals; i++, pas++) {
	    pas->length = pstring_len[i] * sizeof(IIIMP_card16);
	    pas->ptr = p;
	    n = (pstring_len[i] + 1) * sizeof(IIIMP_card16);
	    memcpy(p, pstrs[i], n);
	    p += n;
	}
    }

    if (pstring_len) free(pstring_len);
    return pac;

#undef ROUNDUP
}

static AUXComposed*
create_composed_from_aux_data(
    const aux_data_t *pad1
)
{
    unsigned char *p;
    AUXComposed *pac;
    aux_data_t *pad2;

    int i, n;
    int aux_data_t_n;
    int aux_name_n;
    int integer_list_n, string_list_n;
    int string_n;

#define ROUNDUP(n) ((n + sizeof(int) - 1) / sizeof(int) * sizeof(int))

    /* first of all, caliculate size. */
    n = ROUNDUP(sizeof(AUXComposed));
    aux_data_t_n = n;
    n += sizeof(aux_data_t);
    aux_name_n = n = ROUNDUP(n);
    n += pad1->aux_name_length + sizeof(IIIMP_card16);
    integer_list_n = n = ROUNDUP(n);
    n += pad1->integer_count * sizeof(int);
    string_list_n = n = ROUNDUP(n);
    n += pad1->string_count * sizeof(aux_string_t);
    string_n = n = ROUNDUP(n);
    for (i = 0; i < pad1->string_count; i++) {
	n += pad1->string_list[i].length + sizeof(IIIMP_card16);
    }

    p = (unsigned char*) malloc(n);
    if (!p) return NULL;
    memset(p, 0, n);

    pac = (AUXComposed*) p;
    pac->len = n;
    pad2 = (aux_data_t*)(p + aux_data_t_n);
    pac->pad = pad2;

    *pad2 = *pad1;
    pad2->aux_name = p + aux_name_n;
    memcpy(pad2->aux_name, pad1->aux_name, pad1->aux_name_length);

    if (pad1->integer_count > 0) {
	pad2->integer_list = (int*)(p + integer_list_n);
	memcpy(pad2->integer_list, pad1->integer_list,
	       sizeof(int) * pad1->integer_count);
    } else {
	pad2->integer_list = NULL;
    }

    pad2->string_ptr = p;
    if (pad1->string_count > 0) {
	aux_string_t *pas1, *pas2;

	pas1= pad1->string_list;
	pad2->string_list = pas2 = (aux_string_t*)(p + string_list_n);
	p += string_n;
	for (i = 0; i < pad1->string_count; i++, pas1++, pas2++) {
	    pas2->length = pas1->length;
	    pas2->ptr = p;
	    memcpy(p, pas1->ptr, pas2->length);
	    p += pas2->length + sizeof(IIIMP_card16);
	}
    } else {
	pad2->string_list = NULL;
    }

    return pac;

#undef ROUNDUP
}

/*
 * IM_AUX_START
 */
void
IIimpAuxStart(
    XicCommon ic,
    IIIMCF_event ev
)
{
    aux_t *aux;
    AUXComposed ac;

    TRACE_MESSAGE('A', ("iiimpAux: start\n"));

    aux = aux_get(ic, ev, NULL);
    if (!aux) {
	TRACE_MESSAGE('A', ("iiimpAux: start: failed to get aux\n"));
	return;
    }

    memset(&ac, 0, sizeof(ac));
    ac.aux = aux;
    ac.ev = ev;

    aux->im->ae->dir.method->start(aux, 
				   (XPointer) &ac,
				   /* Always set 0! */
				   0);

    return;
}


/*
 * IM_AUX_DRAW
 */
void
IIimpAuxDraw(
    XicCommon ic,
    IIIMCF_event ev
)
{
    aux_t *aux;
    AUXComposed ac;

    TRACE_MESSAGE('A', ("iiimpAux: draw\n"));

    aux = aux_get(ic, ev, NULL);
    if (!aux) {
	TRACE_MESSAGE('A', ("iiimpAux: draw: failed to get aux\n"));
	return;
    }

    memset(&ac, 0, sizeof(ac));
    ac.aux = aux;
    ac.ev = ev;

    aux->im->ae->dir.method->draw(aux,
				  (XPointer) &ac,
				  /* Always set 0! */
				  0);
    return;
}


/*
 * IM_AUX_DONE
 */
void
IIimpAuxDone(
    XicCommon ic,
    IIIMCF_event ev
)
{
    aux_t *aux;
    AUXComposed ac;

    TRACE_MESSAGE('A', ("iiimpAux: done\n"));

    aux = aux_get(ic, ev, NULL);
    if (!aux) {
	TRACE_MESSAGE('A', ("iiimpAux: done: failed to get aux\n"));
	return;
    }

    memset(&ac, 0, sizeof(ac));
    ac.aux = aux;
    ac.ev = ev;

    aux->im->ae->dir.method->done(aux,
				  (XPointer) &ac,
				  /* Always set 0! */
				  0);
    return;
}

/*
 * IM_AUX_GETVALUES
 */
void
IIimpAuxGetValuesReply(
    XicCommon ic,
    IIIMCF_event ev
)
{
    aux_t *aux;
    AUXComposed ac;

    TRACE_MESSAGE('A', ("iiimpAux: getvalues reply\n"));

    aux = aux_get(ic, ev, NULL);
    if (!aux) {
	TRACE_MESSAGE('A', ("iiimpAux: getvalues reply: failed to get aux\n"));
	return;
    }

    memset(&ac, 0, sizeof(ac));
    ac.aux = aux;
    ac.ev = ev;

    if (aux->im->ae->dir.method->getvalues_reply)
	aux->im->ae->dir.method->getvalues_reply(aux,
						 (XPointer) &ac,
						 /* Always set 0! */
						 0);
    return;
}

#if 0
/*
 * not defined yet
 */
void
IIimpAuxSwitched(
    XicCommon ic,
    XPointer call_data
)
{
    int *		ip;
    int		im_id;
    int		on_off;
    aux_t *		aux;

    TRACE_MESSAGE('A', ("iiimpAux: switched\n"));

    if (NULL == (aux = aux_get(ic, call_data, NULL))) {
	TRACE_MESSAGE('A', ("iiimpAux: switched: failed to get aux\n"));
	return;
    }

    ip = (int *)call_data;
    im_id = *(ip + 0);
    on_off = *(ip + 1);

    aux->im->ae->dir.method->switched(aux, im_id, on_off);
    return;
}
#endif

void
IIimpAuxSetICFocus(
    XicCommon ic
) 
{
    aux_t *aux;
    aux_im_data_t *aux_im;

    aux = XIC_IIIMP(ic, aux);
    if (!aux)
	return;

    for (aux_im = aux->im_list; aux_im; aux_im = aux_im->next) {
	if (aux_im->ae->if_version >= AUX_IF_VERSION_2 &&
	    aux_im->ae->dir.method->set_icforcus != NULL) {
	    aux->im = aux_im;
	    aux_im->ae->dir.method->set_icforcus(aux);
	}
    }
}

void
IIimpAuxUnsetICFocus(
    XicCommon ic
)
{
    aux_t *aux;
    aux_im_data_t *aux_im;

    aux = XIC_IIIMP(ic, aux);
    if (!aux)
	return;

    for (aux_im = aux->im_list; aux_im; aux_im = aux_im->next) {
	if (aux_im->ae->if_version >= AUX_IF_VERSION_2 &&
	    aux_im->ae->dir.method->unset_icforcus != NULL) {
	    aux->im = aux_im;
	    aux_im->ae->dir.method->unset_icforcus(aux);
	}
    }
}

static void
delete_aux_ic(
    XicCommon ic
)
{
    aux_t *aux;
    aux_im_data_t *aux_im;
    aux_ic_info_t *aux_ic;
    aux_ic_info_t *prev_aux_ic;

    aux = XIC_IIIMP(ic, aux);
    if (!aux)
	return;

    for (aux_im = aux->im_list; aux_im; aux_im = aux_im->next) {
	if (aux_im->ae->if_version >= AUX_IF_VERSION_2 &&
	    aux_im->ae->dir.method->destroy_ic != NULL) {
	    aux->im = aux_im;
	    aux_im->ae->dir.method->destroy_ic(aux);
	}
    }

    prev_aux_ic = NULL;
    for (aux_ic = aux_ic_info; aux_ic != NULL; aux_ic = aux_ic->next) {
	if (aux_ic->ic_common == ic) {
	    if (prev_aux_ic == NULL)
		aux_ic_info = aux_ic->next;
	    else
		prev_aux_ic->next = aux_ic->next;
	    Xfree(aux_ic);
	    break;
	}
	prev_aux_ic = aux_ic;
    }
}

void
IIimpDestroryAuxData(
    XicCommon ic
)
{
    aux_t *aux;
    aux_im_data_t *pai1, *pai2;

    if (!ic || !ic->iiimp_icpart) return;
    aux = XIC_IIIMP(ic, aux);
    if (!aux) return;

    delete_aux_ic(ic);

    for (pai1 = aux->im_list; pai1; ) {
	pai2 = pai1->next;
	free(pai1);
	pai1 = pai2;
    }
    aux->im_list = NULL;
}

/*
 * not defined yet
 */
void
IIimpFreeAllAuxData()
{
    aux_handle_t *ah;
    aux_handle_t *ah_next;
    int	i;

    TRACE_MESSAGE('A', ("iiimpAux: destroy\n"));

    for (ah = aux_handle; NULL != ah; ah = ah_next) {
	for (i = 0; i < ah->ae_num; i++) {
	    if (0 == (ah->ae + i)->created) {
		continue;
	    }
	    (ah->ae + i)->dir.method->destroy(NULL);
	    (ah->ae + i)->created = 0;
	}
	ah_next = ah->next;
	Xfree(ah->aux_name.ptr);
	Xfree(ah->file_name);
	dlclose(ah->handle);
	Xfree(ah->ae);
	Xfree(ah);
    }

    aux_handle = NULL;

    return;
}

/*
 *
 */
static aux_handle_t *
aux_load(
    char * aux_file_name
)
{
    int		fd;
    char		buf[64];
    int		magic_len;
    int		len;

    TRACE_MESSAGE('A', ("iiimpAux: aux_load\n"));

    if (-1 == (fd = open(aux_file_name, O_RDONLY, 0))) {
	TRACE_MESSAGE('A', ("iiimpAux: aux_load: can not open %s\n",
			    aux_file_name));
	return NULL;
    }

    magic_len = strlen(AUX_CONF_MAGIC);

    len = read(fd, buf, magic_len);

    close(fd);

    if ((len == magic_len) &&
	(0 == memcmp(buf, AUX_CONF_MAGIC, len))) {
	return aux_conf_load(aux_file_name);
    } else {
	return aux_so_load(aux_file_name);
    }
}


/*
 *
 */
static aux_handle_t *
aux_conf_load(
    char * aux_file_name
)
{
    int		fd;
    struct stat	st_buf;
    void *		addr;
    char *		p;
    char *		aux_name;
    int		aux_name_len;
    char *		aux_so;
    int		aux_so_len;
    aux_handle_t *	ah;
    int		len;
    char *		inbuf;
    size_t		inbytesleft;
    char *		outbuf;
    size_t		outbytesleft;
    int		dir_name_len;
    char *		dir_name_last;

    dir_name_last = strrchr(aux_file_name, '/');
    if (NULL == dir_name_last) {
	return NULL;
    }
    dir_name_len = ((dir_name_last - aux_file_name) + 1);

    if (-1 == (fd = open(aux_file_name, O_RDONLY, 0))) {
	return NULL;
    }

    if (0 != fstat(fd, &st_buf)) {
	close(fd);
	return NULL;
    }

    addr = mmap(0, st_buf.st_size,
		PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);

    close(fd);

    if (MAP_FAILED == addr) {
	return NULL;
    }

    ah = NULL;
	
    for (p = addr, len = st_buf.st_size; 0 < len; ) {
	if ('#' == *p) {
	    while (IS_NOT_EOL(len, p)) {
		p++;
		--len;
	    }
	    if (IS_EOL(len, p)) {
		p++;
		--len;
	    }
	    continue;
	}

	while (IS_SPACE(len, p)) {
	    p++;
	    --len;
	}
	if (IS_EOL(len, p)) {
	    p++;
	    --len;
	    continue;
	}

	aux_name = p;
	while ((!(IS_SPACE(len, p))) && IS_NOT_EOL(len, p)) {
	    p++;
	    --len;
	}
	if (IS_EOL(len, p)) {
	    p++;
	    --len;
	    continue;
	}

	aux_name_len = (p - aux_name);

	while (IS_SPACE(len, p)) {
	    p++;
	    --len;
	}
	if (IS_EOL(len, p)) {
	    p++;
	    --len;
	    continue;
	}

	aux_so = p;
	while ((!(IS_SPACE(len, p))) && IS_NOT_EOL(len, p)) {
	    p++;
	    --len;
	}
	aux_so_len = (p - aux_so);

	ah = (aux_handle_t *)Xmalloc(sizeof (aux_handle_t));
	if (NULL == ah) {
	    break;
	}

	ah->aux_name.len = (aux_name_len * (sizeof (CARD16)));
	ah->aux_name.ptr = Xmalloc(ah->aux_name.len);
	if (NULL == ah->aux_name.ptr) {
	    XFree(ah);
	    break;
	}

	inbuf = aux_name;
	inbytesleft = aux_name_len;
	outbuf = (char *)(ah->aux_name.ptr);
	outbytesleft = ah->aux_name.len;
	IIimpConvertToUTF16(inbuf, inbytesleft,
			    &outbuf, &outbytesleft);

	if ('/' == *aux_so) {
	    ah->file_name = Xmalloc(aux_so_len + 1);
	} else {
	    ah->file_name = Xmalloc(dir_name_len + aux_so_len + 1);
	}
	if (NULL == ah->file_name) {
	    Xfree(ah->aux_name.ptr);
	    Xfree(ah);
	    break;
	}
	if ('/' == *aux_so) {
	    memcpy(ah->file_name, aux_so, aux_so_len);
	    *(ah->file_name + aux_so_len) = '\0';
	} else {
	    memcpy(ah->file_name, aux_file_name, dir_name_len);
	    memcpy(ah->file_name + dir_name_len, aux_so, aux_so_len);
	    *(ah->file_name + dir_name_len + aux_so_len) = '\0';
	}
	ah->handle = NULL;
	ah->ae = NULL;
	ah->ae_num = 0;
	ah->next = aux_handle;
	aux_handle = ah;
    }

    munmap(addr, st_buf.st_size);

    return ah;
}


/*
 *
 */
static aux_dir_t *
get_aux_dir_from_aux_info (void *handle, unsigned int *ifversion)
{
    aux_info_t	*aux_info;

    aux_info = (aux_info_t*)dlsym(handle, AUX_INFO_SYMBOL);

    if(aux_info && aux_info->if_version >= AUX_IF_VERSION_2 &&
	aux_info->register_service != NULL) {
	aux_info->register_service (AUX_IF_VERSION_2, &aux_service);
	*ifversion = aux_info->if_version;
	return aux_info->dir;
    }

	return NULL;
}


/*
 *
 */
static aux_handle_t *
aux_so_load(
    char * aux_file_name
)
{
    void *		handle = (void *)NULL;
    aux_dir_t *	aux_dir;
    aux_dir_t *	ad;
    int		adn;
    aux_handle_t *	ah;
    aux_handle_t *	ah_free;
    int		i;
    unsigned int ifversion;

    TRACE_MESSAGE('A', ("iiimpAux: aux_so_load\n"));

    /*
     * check whether the object is already loaded
     */
    for (ah = aux_handle; NULL != ah; ah = ah->next) {
	if ((0 == strcmp(aux_file_name, ah->file_name)) &&
	    (NULL != ah->handle)) {
	    return ah;
	}
    }

    /*
     * load the object and construct aux_handle_t structure for it
     */
    handle = dlopen(aux_file_name, RTLD_LAZY);
    if (NULL == handle) {
	TRACE_MESSAGE('A',
		      ("iiimpAux: aux_so_load: failed to dlopen %s (%s)\n",
		       aux_file_name, dlerror()));
	return NULL;
    }


    aux_dir = get_aux_dir_from_aux_info (handle, &ifversion);
    if (NULL == aux_dir) {
	aux_dir = (aux_dir_t *)dlsym(handle, AUX_DIR_SYMBOL);
	if (NULL == aux_dir) {
	    dlclose(handle);
	    return NULL;
	}
	ifversion = 0;
    }

    for (adn = 0, ad = aux_dir; 0 < ad->name.len; ad += 1, adn += 1);

    if (NULL == ah) {
	ah = (aux_handle_t *)Xmalloc(sizeof (aux_handle_t));
	if (NULL == ah) {
	    dlclose(handle);
	    return NULL;
	}
	memset(ah, 0, sizeof (aux_handle_t));

	ah_free = ah;
    } else {
	ah_free = NULL;
    }

    if (NULL == ah->file_name) {
	ah->file_name = strdup(aux_file_name);
	if (NULL == ah->file_name) {
	    Xfree(ah);
	    dlclose(handle);
	    return NULL;
	}
    }
    ah->handle = handle;
    ah->ae_num = adn;
    ah->ae = Xmalloc(adn * (sizeof (aux_entry_t)));
    if (NULL == ah->ae) {
	if (NULL != ah_free) {
	    Xfree(ah->file_name);
	    Xfree(ah);
	}
	dlclose(handle);
	return NULL;
    }
    memset(ah->ae, 0, adn * (sizeof (aux_entry_t)));
    for (i = 0; i < adn; i++) {
	(ah->ae + i)->created = 0;
	memcpy(&((ah->ae + i)->dir), aux_dir + i, sizeof (aux_dir_t));
	(ah->ae + i)->if_version = ifversion;
    }

    ah->next = aux_handle;
    aux_handle = ah;

    return ah;
}

static int
auxname_strncmp(
    const IIIMP_card16 *s1,
    const IIIMP_card16 *s2,
    int len
)
{
    len /= sizeof(IIIMP_card16);
    for (;len > 0; len--, s1++, s2++) {
	if (*s1 > *s2) return 1;
	if (*s1 < *s2) return -1;
	if (!*s1) return 0;
    }
    if (!*s1) return 0;
    return 2;
}

static aux_im_data_t*
create_aux_im_data(
    aux_t *aux,
    int id,
    const IIIMP_card16 *auxname
)
{
    aux_entry_t *ae;
    aux_im_data_t *aux_im;
    XicCommon ic;
    XimCommon im;
    IIIMCF_handle handle;
    IIIMF_status st;

    ae = aux_entry_get(auxname);
    if (!ae) return NULL;
    aux_im = (aux_im_data_t *) Xmalloc(sizeof(*aux_im));
    if (!aux_im) return NULL;
    memset(aux_im, 0, sizeof(*aux_im));
    ic = (XicCommon)aux->ic;
    im = (XimCommon)ic->core.im;
    handle = XIM_IIIMP(im, handle);
    st = iiimcf_get_im_id(handle, &aux_im->im_id);
    if (st != IIIMF_STATUS_SUCCESS) {
	XFree(aux_im);
	return NULL;
    }
    /* The following members are only for fake. */
    aux_im->ic_id = id;
    /* end */
    aux_im->ae = ae;

    aux_im->next = aux->im_list;
    aux->im_list = aux_im;
    aux->im = aux_im;
    if (!ae->created) {
	if (!ae->dir.method->create(aux)) return NULL;
	ae->created = 1;
    }

    return aux_im;
}

static aux_t *
aux_get(
    XicCommon ic,
    IIIMCF_event ev,
    const IIIMP_card16 *aux_name
)
{
    IIIMF_status st;
    aux_t *aux;
    aux_im_data_t *aux_im;
    aux_ic_info_t *aux_ic;
    XimCommon im;
    IIIMCF_handle handle;

    TRACE_MESSAGE('A', ("iiimpAux: aux_get\n"));

    if (aux_name == NULL) {
	st = iiimcf_get_aux_event_value(ev, &aux_name,
					NULL, NULL, NULL, NULL, NULL);
	if (st != IIIMF_STATUS_SUCCESS) return NULL;
    }

#if defined(ENABLE_TRACE)
    if (TRACE_P('s')) {
	const IIIMP_card16 *pu;
	TRACE_MESSAGE('s', ("iiimpAux: aux_get: "));
	for (pu = aux_name; *pu; pu++) {
	    TRACE_MESSAGE('s', ("%04x ", *pu));
	}
	TRACE_MESSAGE('s', ("\n"));
    }
#endif /* ENABLE_TRACE */

    /*
     * create aux object if it is not created
     */

    aux = XIC_IIIMP(ic, aux);
    if (!aux) {
	aux = (aux_t *) Xmalloc(sizeof(*aux));
	if (!aux) return NULL;
	memset(aux, 0, sizeof(*aux));
	aux->ic = ic;
	aux->service = &aux_service;
	XIC_IIIMP(ic, aux) = aux;

	aux_ic = Xmalloc(sizeof(*aux_ic));
	if (!aux_ic) return NULL;
	memset(aux_ic, 0, sizeof(*aux_ic));
	im = (XimCommon)ic->core.im;
	handle = XIM_IIIMP(im, handle);
	st = iiimcf_get_im_id(handle, &aux_ic->im_id);
	if (st != IIIMF_STATUS_SUCCESS) {
	    XFree(aux_ic);
	    return NULL;
	}
	aux_ic->ic_id = XIC_IIIMP(ic, id);
	aux_ic->ic_common = ic;
	aux_ic->next = aux_ic_info;
	aux_ic_info = aux_ic;
    }

    if (aux) {
	/*
	 * search for aux_im_data corresponding to aux_name.
	 */
	for (aux_im = aux->im_list; aux_im; aux_im = aux_im->next) {
	    if (!auxname_strncmp(aux_name,
				 aux_im->ae->dir.name.ptr,
				 aux_im->ae->dir.name.len)) {
		aux->im = aux_im;
		return aux;
	    }
	}
    } else {
	if (NULL == (aux = (aux_t *)Xmalloc(sizeof (aux_t)))) {
	    return NULL;
	}
	aux->ic = ic;
	aux->service = &aux_service;
	aux->im = NULL;
	aux->im_list = NULL;
	XIC_IIIMP(ic, aux) = aux;
    }

    aux_im = create_aux_im_data(aux, XIC_IIIMP(ic, id), aux_name);

    if (!aux_im) return NULL;

    return aux;
}

static aux_entry_t *
aux_entry_get(
    const IIIMP_card16 *name
)
{
    aux_handle_t *ah;
    aux_handle_t *ah0;
    aux_entry_t *ae;
    int	i;

    TRACE_MESSAGE('H', ("iiimpAux: aux_entry_get\n"));

    if (!name) return NULL;

    for (ah = aux_handle; NULL != ah; ah = ah->next) {
	if ((ah->aux_name.len > 0)
	    && (!auxname_strncmp(name, ah->aux_name.ptr, ah->aux_name.len))) {
	    /* This handle is created from a configuration file.
	       Load SO now.  */
	    ah0 = aux_so_load(ah->file_name);
	    if (!ah0) continue;
	    ah = ah0;
	}
	for (ae = ah->ae, i = ah->ae_num; 0 < i; ae += 1, --i) {
	    if (!auxname_strncmp(name, ae->dir.name.ptr, ae->dir.name.len)) {
		return ae;
	    }
	}
    }

    return NULL;
}


/*
 *
 */
static aux_im_data_t *
aux_im_get(
    XicCommon ic,
    int im_id
)
{
    aux_im_data_t *	im;
    aux_t *		aux;

    TRACE_MESSAGE('I', ("iiimpAux: aux_im_get\n"));
	
    aux = XIC_IIIMP(ic, aux);

    for (im = aux->im_list; NULL != im; im = im->next) {
	if (im_id == im->im_id) {
	    aux->im = im;
	    return im;
	}
    }

    if (NULL == (im = (aux_im_data_t *)Xmalloc(sizeof (aux_im_data_t)))) {
	return NULL;
    }

    im->ae = NULL;
    im->data = NULL;
    im->next = aux->im_list;
    aux->im_list = im;
    aux->im = im;

    return im;
}

static int
aux_string_length(
    const IIIMP_card16 *str
)
{
    int n;
    for (n = 0; *str; str++, n++);
    return n;
}

/*
 * aux service function
 */

static void
service_aux_setvalue(
    aux_t * aux,
    const unsigned char * p,
    int len
)
{
    AUXComposed *pac = (AUXComposed*) p;

    if (pac->ev) {
	IMAuxSetValues(aux->ic, pac->ev);
    } else if (pac->pad) {
	int i;
	aux_data_t *pad = pac->pad;
	IIIMF_status st;
	IIIMCF_event ev;
	IIIMP_card32 *pintvals;
	const IIIMP_card16 **pstrs;

#ifdef ENABLE_TRACE
	static void show_AC(aux_data_t *pad);
	show_AC(pac->pad);
#endif
	if (pad->integer_count > 0) {
	    pintvals = (IIIMP_card32*) malloc(sizeof(*pintvals) * pad->integer_count);
	    if (!pintvals) return;
	    for (i = 0; i < pad->integer_count; i++) {
		pintvals[i] = pad->integer_list[i];
	    }
	} else {
	    pintvals = NULL;
	}
	if (pad->string_count > 0) {
	    pstrs = (const IIIMP_card16**) malloc(sizeof(*pstrs) * pad->string_count);
	    if (!pstrs) {
		if (pintvals) free(pintvals);
		return;
	    }
	    for (i = 0; i < pad->string_count; i++) {
		pstrs[i] = (const IIIMP_card16*) pad->string_list[i].ptr;
	    }
	} else {
	    pstrs = NULL;
	}

	st = iiimcf_create_aux_setvalues_event((IIIMP_card16*) pad->aux_name,
					       pad->aux_index,
					       pad->integer_count,
					       pintvals,
					       pad->string_count,
					       pstrs,
					       &ev);
	if (st == IIIMF_STATUS_SUCCESS) {
	    IMAuxSetValues(aux->ic, ev);
	}

	if (pintvals) free(pintvals);
	if (pstrs) free(pstrs);
    }

}

static void
service_aux_getvalue(
    aux_t * aux,
    const unsigned char * p,
    int len
)
{
    AUXComposed *pac = (AUXComposed*) p;

    if (pac->ev) {
	IMAuxGetValues(aux->ic, pac->ev);
    } else if (pac->pad) {
	int i;
	aux_data_t *pad = pac->pad;
	IIIMF_status st;
	IIIMCF_event ev;
	IIIMP_card32 *pintvals;
	const IIIMP_card16 **pstrs;

#ifdef ENABLE_TRACE
	static void show_AC(aux_data_t *pad);
	show_AC(pac->pad);
#endif
	if (pad->integer_count > 0) {
	    pintvals = (IIIMP_card32*) malloc(sizeof(*pintvals) * pad->integer_count);
	    if (!pintvals) return;
	    for (i = 0; i < pad->integer_count; i++) {
		pintvals[i] = pad->integer_list[i];
	    }
	} else {
	    pintvals = NULL;
	}
	if (pad->string_count > 0) {
	    pstrs = (const IIIMP_card16**) malloc(sizeof(*pstrs) * pad->string_count);
	    if (!pstrs) {
		if (pintvals) free(pintvals);
		return;
	    }
	    for (i = 0; i < pad->string_count; i++) {
		pstrs[i] = (const IIIMP_card16*) pad->string_list[i].ptr;
	    }
	} else {
	    pstrs = NULL;
	}

	st = iiimcf_create_aux_getvalues_event((IIIMP_card16*) pad->aux_name,
					       pad->aux_index,
					       pad->integer_count,
					       pintvals,
					       pad->string_count,
					       pstrs,
					       &ev);
	if (st == IIIMF_STATUS_SUCCESS) {
	    IMAuxGetValues(aux->ic, ev);
	}

	if (pintvals) free(pintvals);
	if (pstrs) free(pstrs);
    }

}

static int
service_im_id(
    aux_t * aux
)
{
    return aux->im->im_id;
}


static int
service_ic_id(
    aux_t * aux
)
{
    return aux->im->ic_id;
}


static void
service_data_set(
    aux_t * aux,
    int im_id,
    void * data
)
{
    aux_im_data_t *	aux_im;

    for (aux_im = aux->im; NULL != aux_im; aux_im = aux_im->next) {
	if (im_id == aux_im->im_id) {
	    aux_im->data = data;
	}
    }

    return;
}


static void *
service_data_get(
    aux_t * aux,
    int im_id
)
{
    aux_im_data_t *	aux_im;

    for (aux_im = aux->im; NULL != aux_im; aux_im = aux_im->next) {
	if (im_id == aux_im->im_id) {
	    return aux_im->data;
	}
    }

    return NULL;
}


static Display *
service_display(
    aux_t * aux
)
{
    return aux->ic->core.im->core.display;
}


static Window
service_window(
    aux_t * aux
)
{
    if (XIMP_CHK_FOCUSWINMASK(aux->ic)) {
	return aux->ic->core.focus_window;
    } else if (aux->ic->ximp_icpart->value_mask & XIMP_CLIENT_WIN) {
	return aux->ic->core.client_window;
    } else {
	return None;
    }
}


static XPoint *
service_point(
    aux_t * aux,
    XPoint * point
)
{
    if (IS_PREEDIT_POSITION(aux->ic)) {
	point->x = aux->ic->core.preedit_attr.spot_location.x;
	point->y = aux->ic->core.preedit_attr.spot_location.y;
    } else {
	point->x = -1;
	point->y = -1;
    }

    return point;
}


static XPoint *
service_point_caret(
    aux_t * aux,
    XPoint * point
)
{
    point->x = -1;
    point->y = -1;

    if (!IS_PREEDIT_POSITION(aux->ic)) {
	return point;
    }

    PreeditCaretPlacementRelative(aux->ic, point);

    if ((-1 == point->x) && (-1 == point->y)) {
	return service_point(aux, point);
    }

    return point;
}


static size_t
service_utf16_mb(
    const char **	inbuf,
    size_t *	inbytesleft,
    char **		outbuf,
    size_t *	outbytesleft
)
{
    int	r;

    r = IIimpConvertFromUTF16((char *)(*inbuf), *inbytesleft,
			      outbuf, outbytesleft);
    return (size_t)r;
}


static size_t
service_mb_utf16(
    const char **	inbuf,
    size_t *	inbytesleft,
    char **		outbuf,
    size_t *	outbytesleft
)
{
    int	r;

    r = IIimpConvertToUTF16((char *)(*inbuf), *inbytesleft,
			    outbuf, outbytesleft);
    return (size_t)r;
}


static unsigned char *
service_compose(
    const aux_data_t *pad,
    int *size
)
{
    AUXComposed *pac;

    pac = create_composed_from_aux_data(pad);
    return (unsigned char*) pac;
}


static int
service_compose_size(
    aux_data_type_t type,
    const unsigned char *p
)
{
    /* now this function is dummy... */
    return 0;
}

#ifdef ENABLE_TRACE
static void
show_string(
    unsigned char *p,
    int len
)
{
    int i, flag;
    int ch;
    IIIMP_card16 *pu = (IIIMP_card16*) p;

    flag = 0;
    for (i = 0;i < len; i += sizeof(*pu), pu++)	{
	ch = *pu;
	if (ch <= 0x7F) {
	    if (flag)
		TRACE_MESSAGE('A', ("%c", ch));
	    else
		TRACE_MESSAGE('A', (" %c", ch));
	    flag = 1;
	} else {
	    TRACE_MESSAGE('A', (" %X", ch));
	    flag = 0;
	}
    }
    TRACE_MESSAGE('A', ("\n"));
}

static void
show_AC(
    aux_data_t *pad
)
{
    int i;
    TRACE_MESSAGE('A', ("dtype:%d, im:%d, ic:%d, index:%d\n",
			pad->type, pad->im, pad->ic, pad->aux_index));

    TRACE_MESSAGE('A', ("aux_name:"));
    show_string(pad->aux_name, pad->aux_name_length);

    TRACE_MESSAGE('A', ("IL:"));
    for (i = 0; i < pad->integer_count; i++)
	TRACE_MESSAGE('A', ("%d,", pad->integer_list[i]));
    TRACE_MESSAGE('A', ("\n"));

    for (i = 0; i < pad->string_count; i++) {
	TRACE_MESSAGE('A', ("SL(%d):", i));
	show_string(pad->string_list[i].ptr, pad->string_list[i].length);
    }
}
#endif /* ENABLE_TRACE */

static aux_data_t *
service_decompose(
    aux_data_type_t type,
    const unsigned char * p
)
{
    AUXComposed *pac = (AUXComposed*) p;

    if (pac->pad) {
	pac = create_composed_from_aux_data(pac->pad);
	if (!pac) return NULL;
	pac->pad->type = type;
#ifdef ENABLE_TRACE
	show_AC(pac->pad);
#endif
	return pac->pad;
    }

    if (pac->ev) {
	pac = create_composed_from_event(pac->aux, pac->ev);
	if (!pac) return NULL;
	pac->pad->type = type;
#ifdef ENABLE_TRACE
	show_AC(pac->pad);
#endif
	return pac->pad;
    }

    return NULL;
}


static void
service_decompose_free(
    aux_data_t *pad
)
{
    if (!pad) return;
    if (!pad->string_ptr) return;
    free(pad->string_ptr);
}


static void
service_register_X_filter(
    Display *	display,
    Window window,
    int	start_type,
    int	end_type,
    Bool (* filter)(Display *, Window, XEvent *, XPointer),
    XPointer client_data
)
{
    _XRegisterFilterByType(display, window, start_type, end_type,
			   filter, client_data);

    return;
}


static void
service_unregister_X_filter(
    Display *display,
    Window window,
    Bool (* filter)(Display *, Window, XEvent *, XPointer),
    XPointer client_data
)
{
    _XUnregisterFilter(display, window, filter, client_data);
}


static Bool
service_server(
    aux_t * aux
)
{
    char *	client_type;

    client_type = XIM_IIIMP(aux->ic->core.im, client_type);

    return ((0 == strcmp(client_type, "IIIMX XIM Server")) ? True : False);
}


static Window
service_client_window(
    aux_t * aux
)
{
    /* ic is initialized to zero in CreateIC().
     * there is no need not to test
     * (aux->ic->ximp_icpart->value_mask & XIMP_CLIENT_WIN)
     */
    return aux->ic->core.client_window;
}


static Window
service_focus_window(
    aux_t * aux
)
{
    /* ic is initialized to zero in CreateIC().
     * there is no need not to test
     * XIMP_CHK_FOCUSWINMASK(aux->ic)
     */
    return aux->ic->core.focus_window;
}


static int
service_screen_number(
    aux_t * aux
)
{
    return XIC_GUI(aux->ic, screen_number);
}


static int
service_point_screen(
    aux_t * aux,
    XPoint * point
)
{
    Display *	display;
    Window		window;
    Window		root_window;
    Window		child;
    int		screen_number;
    int		x;
    int		y;
    int		new_x;
    int		new_y;

    display = aux->ic->core.im->core.display;

    screen_number = XIC_GUI(aux->ic, screen_number);

    if (!IS_PREEDIT_POSITION(aux->ic)) {
	point->x = -1;
	point->y = -1;
	return screen_number;
    }

    if (XIMP_CHK_FOCUSWINMASK(aux->ic)) {
	window =  aux->ic->core.focus_window;
    } else if (aux->ic->ximp_icpart->value_mask & XIMP_CLIENT_WIN) {
	window = aux->ic->core.client_window;
    } else {
	return -1;
    }

    root_window = RootWindow(display, screen_number);

    x = aux->ic->core.preedit_attr.spot_location.x;
    y = aux->ic->core.preedit_attr.spot_location.y;

    XTranslateCoordinates(display, window, root_window,
			  x, y,
			  &new_x, &new_y, &child);

    point->x = new_x;
    point->y = new_y;

    return screen_number;
}


static int
service_point_caret_screen(
    aux_t * aux,
    XPoint * point
)
{
    point->x = -1;
    point->y = -1;

    if (IS_PREEDIT_POSITION(aux->ic)) {

	PreeditCaretPlacement(aux->ic, point);

	if ((-1 == point->x) && (-1 == point->y)) {
	    return service_point_screen(aux, point);
	}
    }

    return XIC_GUI(aux->ic, screen_number);
}

static Bool
service_get_conversion_mode(aux_t * aux)
{
  XIC xic = (XIC)aux->ic;
  XIMPreeditState preedit_state;

  XVaNestedList preedit_attr = XVaCreateNestedList(0,
						   XNPreeditState, &preedit_state,
						   0);
  XGetICValues((XIC)xic,
	       XNPreeditAttributes, preedit_attr,
	       0);
  XFree(preedit_attr);
  return preedit_state;
}

static void
service_set_conversion_mode(aux_t * aux, int conversion_mode)
{
  XIC xic = (XIC)aux->ic;
  XIMPreeditState preedit_state;

  preedit_state = (conversion_mode == 1 ? XIMPreeditEnable : XIMPreeditDisable);
  XVaNestedList preedit_attr = XVaCreateNestedList(0,
						   XNPreeditState, preedit_state,
						   0);
  XSetICValues((XIC)xic,
	       XNPreeditAttributes, preedit_attr,
	       0);
  XFree(preedit_attr);
}

static aux_t *
service_aux_get_from_id(int im_id, int ic_id,
	CARD16 *aux_name, int aux_name_length)
{
    aux_ic_info_t *aux_ic;

    for (aux_ic = aux_ic_info; aux_ic != NULL; aux_ic = aux_ic->next) {
	if (aux_ic->im_id == im_id && aux_ic->ic_id == ic_id)
	    break;
    }
    if (aux_ic == NULL)
	return NULL;

    return aux_get(aux_ic->ic_common, NULL, aux_name);
}

/* Local Variables: */
/* c-file-style: "iiim-project" */
/* End: */
