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

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>

#include "SunIM.h"
#include "SunIMLock.h"
#ifdef  sun
#include <unistd.h>
#endif

Private iml_inst *iml_make_preedit_start_inst(iml_session);
Private iml_inst *iml_make_preedit_draw_inst(iml_session, IMText *);
Private iml_inst *iml_make_preedit_draw_with_caret_inst(iml_session, IMText *, int);
Private iml_inst *iml_make_preedit_draw_with_chgpos_inst(iml_session_t *, IMText *, int, int, int);
Private iml_inst *iml_make_preedit_erase_inst(iml_session_t *);
Private iml_inst *iml_make_preedit_caret_inst(iml_session_t *, int);
Private iml_inst *iml_make_preedit_done_inst(iml_session_t *);

Private iml_inst *iml_make_status_start_inst(iml_session_t *);
Private iml_inst *iml_make_status_draw_inst(iml_session_t *, IMText *);
Private iml_inst *iml_make_status_done_inst(iml_session_t *);

Private iml_inst *iml_make_lookup_start_inst(iml_session_t *, IMLookupStartCallbackStruct *);
Private iml_inst *iml_make_lookup_draw_inst(iml_session_t *, IMLookupDrawCallbackStruct *);
Private iml_inst *iml_make_lookup_done_inst(iml_session_t *);

Private iml_inst *iml_make_aux_start_inst(iml_session_t *, IMAuxStartCallbackStruct *);
Private iml_inst *iml_make_aux_draw_inst(iml_session_t *, IMAuxDrawCallbackStruct *);
Private iml_inst *iml_make_aux_done_inst(iml_session_t *, IMAuxDoneCallbackStruct *);

Private iml_inst *iml_make_start_conversion_inst(iml_session_t *);
Private iml_inst *iml_make_end_conversion_inst(iml_session_t *);

Private iml_inst *iml_make_commit_inst(iml_session_t *, IMText *);

Private iml_inst *iml_make_keypress_inst(iml_session_t *, IMKeyEventStruct *);

Private iml_inst *iml_link_inst_tail(iml_inst **, iml_inst *);
Public iml_inst *iml_execute_iml_wrapper(iml_session_t *, iml_inst **);

Private void *iml_new(iml_session_t *, int);
Private void *iml_new2(iml_session_t *, int);
Private void *iml_delete(iml_session_t *);
Private void *iml_delete2(iml_session_t *);

Public int iml_add_listener(iml_session_t *, char *, void *, void *); 
Public int iml_delete_listener(iml_session_t *, int);

Public void *iml_getvalue(iml_session_t *, char *, void *);
Public void iml_setvalue(iml_session_t *, char *, void *);

Private void iml_ns_read_ahead(iml_session_t *, int, void *);
Private iml_inst *iml_make_nop_inst(iml_session_t *);
Private char* secure_pathname(char *p);
Private char *lookup_config();

Private void *iml_lfopen(iml_nsc *, char *, char *);
Private size_t iml_lfread(void *, size_t, size_t, void *);
Private size_t iml_lfwrite(void *, size_t, size_t, void *);
Private int iml_lgetfsize(void *);
Private long iml_lftell(void *);
Private int iml_lfseek(void *, long, int);
Private int iml_lfclose(iml_nsc *, void *);

/* TODO !! Needs to be extended to NS_REMOTE 

Private void *iml_rfopen(iml_nsc *, char *, char *);
Private size_t iml_rfread(void *, size_t, size_t, void *);
Private size_t iml_rfwrite(void *, size_t, size_t, void *);
Private int iml_rgetfsize(void *);
Private long iml_rftell(void *);
Private int iml_rfseek(void *, long, int);
Private int iml_rfclose(iml_nsc *, void *);

*/

#define LOCAL_NS_DIR "/var/spool/iiimf/ns"

Public iml_methods_t _iml_methods = {
	iml_make_preedit_start_inst,
	iml_make_preedit_draw_inst,
	iml_make_preedit_draw_with_chgpos_inst,
	iml_make_preedit_erase_inst,
	iml_make_preedit_caret_inst,
	iml_make_preedit_done_inst,
    
	iml_make_status_start_inst,
	iml_make_status_draw_inst,
	iml_make_status_done_inst,
    
	iml_make_lookup_start_inst,
	iml_make_lookup_draw_inst,
	iml_make_lookup_done_inst,
    
	iml_make_start_conversion_inst,
	iml_make_end_conversion_inst,
    
	iml_make_commit_inst,
	iml_make_keypress_inst,
    
	iml_make_aux_start_inst,
	iml_make_aux_draw_inst,
	iml_make_aux_done_inst,
    
	iml_new,
	iml_new2,
	iml_delete,
	iml_delete2,
 
	iml_link_inst_tail,
	iml_execute_iml_wrapper,

	iml_add_listener,
	iml_delete_listener,

        iml_getvalue,
        iml_setvalue,
};

/*-----------------------------------------------------------------
 * Making instance for Preedit region drawing method
 *----------------------------------------------------------------*/
Private iml_inst *
iml_make_preedit_start_inst(
    iml_session_t * s
)
{
    iml_inst *lp;
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst));
    ACTIVATE_REGION(s, PREEDIT);
    lp->opcode = IMM_PREEDIT_START;
    lp->next = NULL;
    lp->size_of_operand = 0;
    return lp;
}

static Bool compare_feedbacks(IMFeedbackList *, IMFeedbackList *);

/* compare two feeback list */

static Bool
compare_feedbacks(
    IMFeedbackList * fbla,
    IMFeedbackList * fblb
)
{
    int la = IM_FEEDBACK_COUNT(fbla);
    IMFeedback *fba = fbla->feedbacks;
    int lb = IM_FEEDBACK_COUNT(fblb);
    IMFeedback *fbb = fblb->feedbacks;
    IMFeedback *fa, *fb;
    int i, j, type, value;
    int exist;
    
    /* return False if lengths are not same */
    if (la != lb) {
        return False;
    }
    /* compare each feedback */
    for (i = 0; i < la; i++) {
        fa = &fba[i];
        type = IM_FEEDBACK_TYPE(fa);
        value = IM_FEEDBACK_VALUE(fa);
        exist = 0;
        for (j = 0; j < lb; j++) {
            fb = &fbb[j];
            if (type == IM_FEEDBACK_TYPE(fb)) {
                if (value != IM_FEEDBACK_VALUE(fb)) {
                    return False;
                }
                exist = 1;
            }
        }
        /* type does not exist in b */
        if (exist == 0) {
            return False;
        }
    }
    return True;
}

Private iml_inst *
iml_make_preedit_draw_with_caret_inst(
    iml_session_t * s,
    IMText * text,	/* current text */
    int caret
)
{
    iml_inst *lp;
    IMPreeditDrawCallbackStruct *p;
    
    UTFCHAR *cws;		/* current str */
    IMFeedbackList *cfb;	/* current feedback */
    UTFCHAR *pws;		/* prev str */
    int pwl;			/* prev len */
    IMFeedbackList *pfb;	/* prev fb */
    int cwl;			/* current len */
    int si;			/* str index */
    int sri;			/* str reverse index */
    int fbi;			/* fb index */
    int fbri;			/* fb reverse index */
    int i;			/* index */
    int j;
    int ri;
    
    if (text && text->encoding != UTF16_CODESET) {
        return (iml_make_nop_inst(s));
    }
    if (!text || text->char_length <= 0) {
        return (iml_make_preedit_erase_inst(s));
    }
    cws = text->text.utf_chars;
    cwl = text->char_length;
    cfb = text->feedback;
    
    if (!cfb) {
        return (iml_make_nop_inst(s));
    }
    pws = s->PreEditTextInfo.text->text.utf_chars;
    pwl = s->PreEditTextInfo.text->char_length;
    pfb = s->PreEditTextInfo.text->feedback;
    
#ifdef	DEBUG
    printf("## CWS ##\n");
    //list_imtext(text);
    
    printf("## PWS ##\n");
    //list_imtext(s->PreEditTextInfo.text);
#endif
    
    ri = pwl;
    
    /* Compare by utf_chars */
    for (si = 0; si < pwl; si++) {
        if (pws[si] != cws[si]) {
            break;
        }
    }
    
    if (cwl && (pwl == cwl)) {
        for (sri = pwl - 1; sri >= 0; sri--) {
            if (pws[sri] != cws[sri]) {
                break;
            }
        }
        for (fbri = pwl - 1; fbri >= 0; fbri--) {
            if (compare_feedbacks(&pfb[fbri], &cfb[fbri]) == False) {
                break;
            }
        }
        ri = _MAX(sri, fbri);
    }
#ifdef	DEBUG
    printf("## PWS ##\n");
    //list_feedback(pwl, pfb);
    
    printf("## CWS ##\n");
    //list_feedback(pwl, cfb);
#endif
    
    for (fbi = 0; fbi < pwl; fbi++) {
        if (compare_feedbacks(&pfb[fbi], &cfb[fbi]) == False) {
            break;
        }
    }
    
    i = _MIN(si, fbi);
    
    if (caret == -1 && pwl == cwl && i == cwl) {
        /*
	 * Current preedit is the same as previous
	 */
        return (iml_make_nop_inst(s));
    }

    lp = (iml_inst *) s->If->m->iml_new(s,
					sizeof(iml_inst)
					+ sizeof(IMPreeditDrawCallbackStruct));
    lp->opcode = IMM_PREEDIT_DRAW;
    lp->next = NULL;
    
    p = (IMPreeditDrawCallbackStruct *) & (lp->operand);
    p->text = (IMText *) s->If->m->iml_new(s, sizeof(IMText));
    p->text->encoding = UTF16_CODESET;
    p->text->feedback = cfb + i;
    p->text->count_annotations = 0;
    p->text->annotations = 0;
    if ((p->text->char_length = cwl - i) <= 0) {
        if (cwl < pwl) {
            p->text = NULL;
        } else {
            p->text->text.utf_chars = NULL;
        }
    } else {
        p->text->text.utf_chars = cws + i;
    }
    p->chg_first = i;
    p->chg_length = pwl - i;
    
    if (caret == -1) {
        p->caret = cwl;		/* iml_make_preedit_draw_inst() */
    } else {
        p->caret = caret;	/* caret is specified */
    }
    
#ifdef	DEBUG
    printf("iml_make_preedit_draw_with_caret_inst()\n");
    printf("	chg_first=%d\n", p->chg_first);
    printf("	chg_length=%d\n", p->chg_length);
    printf("	caret=%d\n", p->caret);
#endif
    
#ifdef	IML_API_TEST
    /*
     * testing for text deletion and insertion
     */
    return iml_make_preedit_draw_with_chgpos_inst(
        s,
        p->text,
        p->chg_first,
        p->chg_length,
        p->caret);
#endif
    
    /* buffer overflow */
    if ((cwl + 1) > s->PreEditTextBufferSize) {
        IMFeedbackList *flist;
        free(s->PreEditTextInfo.text->text.utf_chars);
        free(s->PreEditTextInfo.text->feedback);
        for (i = 0; i < s->PreEditTextBufferSize; i++) {
            flist = &s->PreEditTextInfo.text->feedback[i];
            if (flist) {
                free(flist->feedbacks);
            }
        }
        free(s->PreEditTextInfo.text->feedback);
        s->PreEditTextInfo.text->text.utf_chars = (UTFCHAR *) calloc(1, sizeof(UTFCHAR) * (cwl + 1));
        
        s->PreEditTextInfo.text->feedback = (IMFeedbackList *) calloc(1, sizeof(IMFeedbackList) * (cwl + 1));
        for (i = 0; i < cwl + 1; i++) {
            flist = &s->PreEditTextInfo.text->feedback[i];
            flist->feedbacks = (IMFeedback *) calloc(1, sizeof(IMFeedback) * DEFAULTFeedbackSize);
        }
        
        s->PreEditTextBufferSize = cwl + 1;
        s->PreEditAttrBufferSize = cwl + 1;
    }
    s->PreEditTextInfo.text->char_length = cwl;
    memcpy(s->PreEditTextInfo.text->text.utf_chars, cws, cwl * sizeof(UTFCHAR));
    
    /*
     * save current string as previous string
     */
    for (i = 0; i < cwl; i++) {
        IMFeedbackList *fblp = &s->PreEditTextInfo.text->feedback[i];
        IMFeedbackList *fblc = &cfb[i];
        fblp->count_feedbacks = fblc->count_feedbacks;
        for (j = 0; j < fblc->count_feedbacks; j++) {
            IMFeedback *fbp = &fblp->feedbacks[j];
            IMFeedback *fbc = &fblc->feedbacks[j];
            IM_FEEDBACK_TYPE(fbp) = IM_FEEDBACK_TYPE(fbc);
            IM_FEEDBACK_VALUE(fbp) = IM_FEEDBACK_VALUE(fbc);
        }
    }
    return lp;
}

Private iml_inst *
iml_make_preedit_draw_inst(
    iml_session_t * s,
    IMText * text
)
{
    if (text && text->encoding != UTF16_CODESET) {
        return (iml_make_nop_inst(s));
    }
    return iml_make_preedit_draw_with_caret_inst(s, text, -1);
}

Private iml_inst *
iml_make_preedit_draw_with_chgpos_inst(
    iml_session_t * s,
    IMText * text,
    int chg_pos,
    int chg_len,
    int caret
)
{
    iml_inst *lp;
    IMPreeditDrawCallbackStruct *p;
    UTFCHAR *preedit_buf;
    IMFeedbackList *feedback_buf;
    int cwl;			/* current len */
    int i = 0;
    int j;
    
    if (text && text->encoding != UTF16_CODESET) {
        return (iml_make_nop_inst(s));
    }
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) + sizeof(IMPreeditDrawCallbackStruct));
    lp->opcode = IMM_PREEDIT_DRAW;
    lp->next = NULL;
    
    p = (IMPreeditDrawCallbackStruct *) & (lp->operand);
    p->text = (IMText *) s->If->m->iml_new(s, sizeof(IMText));
    
    if (text == NULL) {
        /*
	 * deletion
	 */
        p->text = NULL;
    } else {
        /* feedback is required */
        if (!text->feedback) {
            return (iml_make_nop_inst(s));
        }
        p->text->encoding = UTF16_CODESET;
        p->text->text.utf_chars = text->text.utf_chars;
        p->text->char_length = text->char_length;
        p->text->feedback = text->feedback;
        p->text->count_annotations = text->count_annotations;
        p->text->annotations = text->annotations;
    }
    
    p->chg_first = chg_pos;
    p->chg_length = chg_len;
    p->caret = caret;
    
#ifdef	DEBUG
    printf("iml_make_preedit_draw_with_chgpos_inst()\n");
    if (text != NULL) {
        printf("	utf_char_length=%d\n", p->text->char_length);
    }
    printf("	chg_first=%d\n", p->chg_first);
    printf("	chg_length=%d\n", p->chg_length);
    printf("	caret=%d\n", p->caret);
#endif
    
    preedit_buf = (UTFCHAR *) s->If->m->iml_new(s, sizeof(UTFCHAR) * DEFAULTPreEditTextBufferSize);
    feedback_buf = (IMFeedbackList *) s->If->m->iml_new(s, sizeof(IMFeedbackList) * DEFAULTPreEditAttrBufferSize);
    
    cwl = 0;
    
    for (i = 0; i < p->chg_first; i++, cwl++) {
        preedit_buf[cwl] = s->PreEditTextInfo.text->text.utf_chars[i];
        feedback_buf[cwl] = s->PreEditTextInfo.text->feedback[i];
    }
    if (text != NULL) {
        for (i = 0; i < p->text->char_length; i++, cwl++) {
            preedit_buf[cwl] = p->text->text.utf_chars[i];
            feedback_buf[cwl] = p->text->feedback[i];
        }
    }
    for (i = p->chg_first + p->chg_length; i < s->PreEditTextInfo.text->char_length; i++, cwl++) {
        preedit_buf[cwl] = s->PreEditTextInfo.text->text.utf_chars[i];
        feedback_buf[cwl] = s->PreEditTextInfo.text->feedback[i];
    }
    preedit_buf[cwl] = 0;
    
    /*
     * save current string as previous string
     */
    if ((cwl + 1) > s->PreEditTextBufferSize) {
        free(s->PreEditTextInfo.text->text.utf_chars);
        free(s->PreEditTextInfo.text->feedback);
        s->PreEditTextInfo.text->text.utf_chars = (UTFCHAR *) calloc(sizeof(UTFCHAR), cwl + 1);
        s->PreEditTextInfo.text->feedback = (IMFeedbackList *) calloc(sizeof(IMFeedbackList), cwl);
        s->PreEditTextBufferSize = cwl + 1;
        s->PreEditAttrBufferSize = cwl + 1;
    }
    s->PreEditTextInfo.text->char_length = cwl;
    
    memcpy(s->PreEditTextInfo.text->text.utf_chars, preedit_buf,
	   cwl * sizeof(UTFCHAR));

    for (i = 0; i < cwl; i++) {
        IMFeedbackList *fblp = &s->PreEditTextInfo.text->feedback[i];
        IMFeedbackList *fblc = &feedback_buf[i];
        fblp->count_feedbacks = fblc->count_feedbacks;
        for (j = 0; j < fblc->count_feedbacks; j++) {
            IMFeedback *fbp = &fblp->feedbacks[j];
            IMFeedback *fbc = &fblc->feedbacks[j];
            IM_FEEDBACK_TYPE(fbp) = IM_FEEDBACK_TYPE(fbc);
            IM_FEEDBACK_VALUE(fbp) = IM_FEEDBACK_VALUE(fbc);
        }
    }
    return lp;
}

Private iml_inst *
iml_make_preedit_caret_inst(
    iml_session_t * s,
    int caret
)
{
    IMPreeditDrawCallbackStruct *p;
    iml_inst *lp;
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) + sizeof(IMPreeditDrawCallbackStruct));
    lp->opcode = IMM_PREEDIT_DRAW;
    lp->next = NULL;
    
    p = (IMPreeditDrawCallbackStruct *) & (lp->operand);
    p->text = NULL;		/* delete */
    p->chg_first = 0;
    p->chg_length = 0;		/* nothing will be changed */
    p->caret = caret;		/* only set caret position */
    
    return lp;
}

Private iml_inst *
iml_make_preedit_erase_inst(
    iml_session_t * s
)
{
    IMPreeditDrawCallbackStruct *p;
    iml_inst *lp;
    
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) + sizeof(IMPreeditDrawCallbackStruct));
    lp->opcode = IMM_PREEDIT_DRAW;
    lp->next = NULL;
    p = (IMPreeditDrawCallbackStruct *) & (lp->operand);
    p->chg_first = 0;
    p->text = (IMText *) s->If->m->iml_new(s, sizeof(IMText));
    
    p->text->encoding = UTF16_CODESET;
    p->text->text.utf_chars = NULL;
    p->text->char_length = 0;
    p->text->feedback = NULL;
    p->text->count_annotations = 0;
    p->text->annotations = NULL;
    
    p->chg_length = s->PreEditTextInfo.text->char_length;	/* previous length */
    s->PreEditTextInfo.text->char_length = 0;
    p->caret = 0;
    return lp;
}

Private iml_inst *
iml_make_preedit_done_inst(
    iml_session_t * s
)
{
    iml_inst *lp;
    
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst));
    lp->opcode = IMM_PREEDIT_DONE;
    lp->next = NULL;
    lp->size_of_operand = 0;
    INACTIVATE_REGION(s, PREEDIT);
    return lp;
}

/*-----------------------------------------------------------------
 * Making instance for Status region drawing method
 *----------------------------------------------------------------*/
Private iml_inst *
iml_make_status_start_inst(
    iml_session_t * s
)
{
    iml_inst *lp;
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst));
    ACTIVATE_REGION(s, STATUS);
    lp->opcode = IMM_STATUS_START;
    lp->next = NULL;
    lp->size_of_operand = 0;
    return lp;
}

Private iml_inst *
iml_make_status_draw_inst(
    iml_session_t * s,
    IMText * text
)
{
    int i, j;
    iml_inst *lp;
    IMStatusDrawCallbackStruct *q;

    if (text == NULL) {
        return iml_make_nop_inst(s);
    }

    if (text->encoding != UTF16_CODESET) {
        return iml_make_nop_inst(s);
    }
    
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) + sizeof(IMStatusDrawCallbackStruct));
    
    lp->opcode = IMM_STATUS_DRAW;
    lp->next = NULL;

    q = (IMStatusDrawCallbackStruct *) & lp->operand;
    q->text = (IMText *) s->If->m->iml_new(s, sizeof(IMText));
    
    q->text->encoding = UTF16_CODESET;
    q->text->text.utf_chars = text->text.utf_chars;
    q->text->char_length = text->char_length;
    q->text->feedback = text->feedback;
    q->text->count_annotations = 0;
    q->text->annotations = NULL;
    
    /* copy feedback */
    for (i = 0; i < text->char_length; i++) {
        IMFeedbackList *fblfrom = &text->feedback[i];
        IMFeedbackList *fblto = &s->status_cache.text->feedback[i];
        int fcount = IM_FEEDBACK_COUNT(fblfrom);
	IM_FEEDBACK_COUNT(fblto) = IM_FEEDBACK_COUNT(fblfrom);
        for (j = 0; j < fcount; j++) {
            IMFeedback *fbfrom = &fblfrom->feedbacks[j];
            IMFeedback *fbto = &fblto->feedbacks[j];
	    IM_FEEDBACK_TYPE(fbto) = IM_FEEDBACK_TYPE(fbfrom);
	    IM_FEEDBACK_VALUE(fbto) = IM_FEEDBACK_VALUE(fbfrom);
        }
    }
    memcpy(s->status_cache.text->text.utf_chars,
	   text->text.utf_chars,
	   sizeof(UTFCHAR) * text->char_length);
    s->status_cache.text->char_length = text->char_length;
    
    return lp;
}

Private iml_inst *
iml_make_status_done_inst(
    iml_session_t * s
)
{
    iml_inst *lp;
    
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst));
    lp->opcode = IMM_STATUS_DONE;
    lp->next = NULL;
    lp->size_of_operand = 0;
    INACTIVATE_REGION(s, STATUS);
    return lp;
}

/*-----------------------------------------------------------------
 * Making instance for Aux region drawing method
 *----------------------------------------------------------------*/
Private iml_inst *
iml_make_aux_start_inst(
    iml_session_t * s,
    IMAuxStartCallbackStruct * a
)
{
    iml_inst *lp = (iml_inst *) 0;
    IMAuxStartCallbackStruct *ls;
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) + sizeof(IMAuxStartCallbackStruct));
    lp->opcode = IMM_AUX_START_2;
    lp->next = NULL;
    ls = (IMAuxStartCallbackStruct *) & lp->operand;
    ls->aux_name = (char *) a->aux_name;
    ls->aux_index = a->aux_index;
    return lp;
}

Private iml_inst *
iml_make_aux_draw_inst(
    iml_session_t * s,
    IMAuxDrawCallbackStruct * a
)
{
    iml_inst *lp = (iml_inst *) 0;
    IMAuxDrawCallbackStruct *ls;
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) +
					sizeof(IMAuxDrawCallbackStruct));
    lp->opcode = IMM_AUX_DRAW_2;
    lp->next = NULL;
    ls = (IMAuxDrawCallbackStruct *) & lp->operand;
    
    ls->aux_name = (char *) a->aux_name;
    ls->aux_index = a->aux_index;
    ls->count_integer_values = a->count_integer_values;
    ls->integer_values = a->integer_values;
    ls->count_string_values = a->count_string_values;
    ls->string_values = a->string_values;

    return lp;
}

Private iml_inst *
iml_make_aux_done_inst(
    iml_session_t * s,
    IMAuxDoneCallbackStruct * a
)
{
    iml_inst *lp = (iml_inst *) 0;
    IMAuxDoneCallbackStruct *ls;
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) +
					sizeof(IMAuxDoneCallbackStruct));
    lp->opcode = IMM_AUX_DONE_2;
    lp->next = NULL;
    ls = (IMAuxDoneCallbackStruct *) & lp->operand;
    
    ls->aux_name = (char *) a->aux_name;
    ls->aux_index = a->aux_index;
    
    return lp;
}

/*-----------------------------------------------------------------
 * Making instance for Lookup choice region drawing method
 *----------------------------------------------------------------*/

Private iml_inst *
iml_make_lookup_start_inst(
    iml_session_t * s,
    IMLookupStartCallbackStruct * start
)
{
    iml_inst *lp;
    IMLookupStartCallbackStruct *ls;
    if (start == NULL) {
        lp = iml_make_nop_inst(s);
        return lp;
    }
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) +
					sizeof(IMLookupStartCallbackStruct));
    lp->opcode = IMM_LOOKUP_START | IMM_CB_RESULT_REQUIRED;
    lp->next = NULL;
    ACTIVATE_REGION(s, LOOKUP);
    ls = (IMLookupStartCallbackStruct *) & lp->operand;
    ls->event = NULL;
    ls->whoIsMaster = start->whoIsMaster;
    ls->IMPreference = start->IMPreference;
    ls->CBPreference = NULL;
    return lp;
}

Private iml_inst *
iml_make_lookup_draw_inst(
    iml_session_t * s,
    IMLookupDrawCallbackStruct * draw
)
{
    iml_inst *lp;
    register IMLookupDrawCallbackStruct *ld;
    int i;
    
    if (draw == NULL) {
        return iml_make_nop_inst(s);
    }
    lp = (iml_inst *) s->If->m->iml_new(s,
					sizeof(iml_inst) + sizeof(IMLookupDrawCallbackStruct));
    lp->opcode = IMM_LOOKUP_DRAW;
    lp->next = NULL;
    ld = (IMLookupDrawCallbackStruct *) & lp->operand;
    
    ld->n_choices = draw->n_choices;
    ld->index_of_first_candidate = draw->index_of_first_candidate;
    ld->index_of_last_candidate = ld->n_choices - 1;
    ld->index_of_last_candidate = draw->index_of_last_candidate;
    ld->index_of_current_candidate = draw->index_of_current_candidate;
    ld->choices = (IMChoiceObject *) s->If->m->iml_new(s, ld->n_choices * sizeof(IMChoiceObject));
    ld->max_len = draw->max_len;
    ld->title = draw->title;
    
    for (i = 0; i < ld->n_choices; i++) {
        (ld->choices)[i].value = (draw->choices)[i].value;
        (ld->choices)[i].label = draw->choices[i].label;
    }
    
    return lp;
}

Private iml_inst *
iml_make_lookup_done_inst(
    iml_session_t * s
)
{
    iml_inst *lp;
    
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst));
    lp->opcode = IMM_LOOKUP_DONE;
    lp->next = NULL;
    lp->size_of_operand = 0;
    INACTIVATE_REGION(s, LOOKUP);
    return lp;
}

Private iml_inst *
iml_make_commit_inst(
    iml_session_t * s,
    IMText * text	/* byte committed string */
)
{
    iml_inst *rv;
    IMText *cs;
    
    if (!text || text->text.utf_chars == NULL || text->char_length <= 0) {
        return iml_make_nop_inst(s);
    }

    if(text->encoding != UTF16_CODESET){
        return iml_make_nop_inst(s);
    }

    if (text->char_length == 1 &&
	(text->text.utf_chars[0] < 0x20 || text->text.utf_chars[0] == 0x7f)) {
        IMKeyEventStruct *key = (IMKeyEventStruct *) s->If->m->iml_new(s, sizeof(IMKeyEventStruct));
        key->keyCode = text->text.utf_chars[0];
        key->keyChar = text->text.utf_chars[0];
        key->modifier = 0;
        key->time_stamp = 0;
        return iml_make_keypress_inst(s, key);
    }
    rv = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) + sizeof(IMText));
    rv->opcode = IMM_COMMIT;
    rv->next = NULL;
    rv->size_of_operand = sizeof(IMText);
    cs = (IMText *) & rv->operand;
    cs->encoding = UTF16_CODESET;
    cs->char_length = text->char_length;
    cs->text.utf_chars = text->text.utf_chars;
    cs->feedback = text->feedback;
    cs->count_annotations = text->count_annotations;
    cs->annotations = text->annotations;

    if (text->text.utf_chars[text->char_length - 1] < 0x20) {
        IMText *achar = (IMText *) s->If->m->iml_new(s, sizeof(IMText));
        achar->encoding = UTF16_CODESET;
        achar->char_length = 1;
        achar->text.utf_chars = (UTFCHAR *) s->If->m->iml_new(s, sizeof(UTFCHAR));
        achar->text.utf_chars[0] = text->text.utf_chars[text->char_length - 1];
        achar->feedback = NULL;
        achar->count_annotations = 0;
        achar->annotations = NULL;
        cs->char_length--;
        rv->next = s->If->m->iml_make_commit_inst(s, achar);
    }

    return rv;
}

/*-----------------------------------------------------------------
 *  Making instance for another method
 *----------------------------------------------------------------*/

Private iml_inst *
iml_make_start_conversion_inst(
    iml_session_t * s
)
{
    iml_status_t *status;
    iml_inst *lp;
    s->public_status |= IMLSTATUS_Henkan_Mode;
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) + sizeof(iml_status_t));
    lp->opcode = IMM_SET_STATUS;
    lp->next = NULL;
    status = (iml_status_t *) & (lp->operand);
    *status = s->public_status;
    return lp;
}

Private iml_inst *
iml_make_end_conversion_inst(
    iml_session_t * s
)
{
    iml_status_t *status;
    iml_inst *lp;
    s->public_status &= ~IMLSTATUS_Henkan_Mode;
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) + sizeof(iml_status_t));
    lp->opcode = IMM_SET_STATUS;
    lp->next = NULL;
    status = (iml_status_t *) & (lp->operand);
    *status = s->public_status;
    return lp;
}

/*-----------------------------------------------------------------
 *  keypress
 *----------------------------------------------------------------*/
Private iml_inst *
iml_make_keypress_inst(
    iml_session_t * s,
    IMKeyEventStruct * key
)
{
    iml_inst *rv;
    int sz = sizeof(IMKeyEventStruct) + 1;
    IMKeyEventStruct *kp;
    
    rv = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) + sz);
    rv->opcode = IMM_KEYPRESS;
    rv->next = NULL;
    rv->size_of_operand = sz;
    
    kp = (IMKeyEventStruct *) & rv->operand;
    
#ifdef	DEBUG
    printf("iml_make_keypress_inst() keycode=%x\n", key->keyCode);
#endif
    
    kp->keyCode = key->keyCode;
    kp->keyChar = key->keyChar;
    kp->modifier = key->modifier;
    kp->time_stamp = key->time_stamp;
    
    return rv;
}


/*-----------------------------------------------------------------
 *  another convenient method
 *----------------------------------------------------------------*/

Private iml_inst *
iml_link_inst_tail(
    iml_inst ** rrv,
    iml_inst * lp
)
{
    register iml_inst *clp = *rrv;
    
    if (!(*rrv)) {
        *rrv = lp;
    } else {
        if (clp) {
            while (clp->next) {
                clp = clp->next;
            }
            clp->next = lp;
        } else {
            clp = lp;
        }
    }
    return *rrv;
}

/*-----------------------------------------------------------------
 *  Private Use Only
 *----------------------------------------------------------------*/

Private iml_inst *
iml_make_nop_inst(
    iml_session_t * s
)
{
    iml_inst *lp;
    
    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst));
    lp->opcode = IMM_NOP;
    lp->next = NULL;
    lp->size_of_operand = 0;
    return lp;
}

/*-----------------------------------------------------------------
 *  Public sub routines
 *----------------------------------------------------------------*/

Public iml_methods_t *
_iml_dup_iml_methods()
{
    iml_methods_t *m = (iml_methods_t *) malloc(sizeof(_iml_methods));
    memcpy(m, &_iml_methods, sizeof(_iml_methods));
    return m;
}

#define ALIGN16(p)		(((p) & 0x0f) ? (((p) & ~(0x0f)) + 0x10) : (p))
#define MEM_BLOCK_SIZE		(128 * 1024)
#define MEM_BLOCK_HEADER_SIZE	(ALIGN16(sizeof (iml_inst_mem_block_list_t)))
#define MEM_BLOCK_MAX_SIZE	(MEM_BLOCK_SIZE - MEM_BLOCK_HEADER_SIZE)
#define MEM_ELEM_HEADER_SIZE	(ALIGN16(sizeof (iml_inst_mem_elem_list_t)))

struct _iml_inst_mem_block_list {
    /* memory block header */
    size_t size;
    size_t used;
    size_t rest;
    struct _iml_inst_mem_block_list *	next;

    /* memory block */
};

struct _iml_inst_mem_elem_list {
    /* memory header */
    struct _iml_inst_mem_elem_list *	next;

    /* memory */
};

Private void * slot_manager_alloc_small(iml_session_t * s, int size);
Private void * slot_manager_alloc_mem_elem(iml_session_t * s, int size);
Private void * slot_manager_alloc_mem_elem2(iml_session_t * s, int size);
Private void * slot_manager_free(iml_session_t * s);
Private void * slot_manager_free2(iml_session_t * s);

Private void *
iml_new(
    iml_session_t * s,
    int size
)
{
    size = ALIGN16(size);
    if (MEM_BLOCK_MAX_SIZE < size) {
	return slot_manager_alloc_mem_elem(s, size);
    } else {
	return slot_manager_alloc_small(s, size);
    }
}

Private void *
iml_new2(
    iml_session_t * s,
    int size
)
{
    size = ALIGN16(size);
    return slot_manager_alloc_mem_elem2(s, size);
}

Private void *
iml_delete(
    iml_session_t * s
)
{
    return slot_manager_free(s);
}

Public void *
iml_delete2(
    iml_session_t * s
)
{
    return slot_manager_free2(s);
}

#ifdef	sun
mutex_t		memory_block_mutex;
#elif	WIN32
HANDLE		memory_block_mutex;
#else	/* Linux */
pthread_mutex_t	memory_block_mutex;
#endif

Public void
sunim_slot_manager_init()
{
#ifdef	sun
#elif   WIN32
    if ((memory_block_mutex = CreateMutex(NULL, FALSE, NULL)) == 0) {
        printf("WIN32 Thread: CreateMutex() ERROR\n");
        return;
    }
#else	/* Linux */
#endif
}

static iml_inst_mem_block_list_t * memory_block_list;

Private void *
slot_manager_alloc_small(
    iml_session_t * s,
    int size
)
{
    iml_inst_mem_block_list_t * mbl;
    void * retp;

    mbl = s->mem_block_short_term_small;
    while (NULL != mbl) {
	if (size <= mbl->rest) {
	    if (mbl->rest < 1024) {
		retp = 0;
	    }
	    retp = (((char *)mbl) + mbl->used);

	    mbl->used += size;
	    mbl->rest -= size;

	    return retp;
	}

	mbl = mbl->next;
    }

    SUNIM_LOCK(memory_block_mutex);

    if (NULL == memory_block_list) {
	SUNIM_UNLOCK(memory_block_mutex);

	mbl = malloc(MEM_BLOCK_SIZE);
	if (NULL == mbl) {
	    return NULL;
	}
    } else {
	mbl = memory_block_list;
	memory_block_list = mbl->next;

	SUNIM_UNLOCK(memory_block_mutex);
    }

    mbl->used = MEM_BLOCK_HEADER_SIZE;
    mbl->size = (MEM_BLOCK_SIZE - mbl->used);
    mbl->rest = (MEM_BLOCK_SIZE - mbl->used);
    mbl->next = s->mem_block_short_term_small;
    s->mem_block_short_term_small = mbl;

    return slot_manager_alloc_small(s, size);
}

Private void *
slot_manager_alloc_mem_elem(
    iml_session_t * s,
    int size
)
{
    iml_inst_mem_elem_list_t * mel;

    size += MEM_ELEM_HEADER_SIZE;

    mel = malloc(size);
    if (NULL == mel) {
	return NULL;
    }

    mel->next = s->mem_elem_short_term;
    s->mem_elem_short_term = mel;

    return (((char *)mel) + MEM_ELEM_HEADER_SIZE);
}

Private void *
slot_manager_alloc_mem_elem2(
    iml_session_t * s,
    int size
)
{
    iml_inst_mem_elem_list_t * mel;

    size += MEM_ELEM_HEADER_SIZE;

    mel = malloc(size);
    if (NULL == mel) {
	return NULL;
    }

    mel->next = s->mem_elem_long_term;
    s->mem_elem_long_term = mel;

    return (((char *)mel) + MEM_ELEM_HEADER_SIZE);
}

Private void *
slot_manager_free(
    iml_session_t * s
)
{
    iml_inst_mem_block_list_t * mbl;
    iml_inst_mem_elem_list_t * mel;
    iml_inst_mem_elem_list_t * mel_n;

    mbl = s->mem_block_short_term_small;
    if (NULL != mbl) {
	while ((NULL != mbl->next)) {
	    mbl = mbl->next;
	}

	SUNIM_LOCK(memory_block_mutex);
	mbl->next = memory_block_list;
	memory_block_list = s->mem_block_short_term_small;
	SUNIM_UNLOCK(memory_block_mutex);

	s->mem_block_short_term_small = NULL;
    }

    mel = s->mem_elem_short_term;
    while (NULL != mel) {
	mel_n = mel->next;
	free(mel);
	mel = mel_n;
    }

    s->mem_elem_short_term = NULL;

    return NULL;
}

Private void *
slot_manager_free2(
    iml_session_t * s
)
{
    iml_inst_mem_elem_list_t * mel;
    iml_inst_mem_elem_list_t * mel_n;

    mel = s->mem_elem_long_term;
    while (NULL != mel) {
	mel_n = mel->next;
	free(mel);
	mel = mel_n;
    }

    s->mem_elem_long_term = NULL;

    return NULL;
}

Public void *
iml_getvalue(
    iml_session_t * s, /* in */
    char* attribute,   /* in */
    void* value        /* in */
)
{
    iml_nsc *nsc;
    char* location;

    if(!strcmp("namespacecontext", attribute)){
        char* category = (char*) attribute ;

        nsc = (iml_nsc *) calloc(1, sizeof(iml_nsc));
        nsc->identifier = (char *)value; /* such as "unit" or "unit.hindi" */
        location = lookup_config(category, nsc->identifier);

        if(!strcmp(location, NS_LOCAL)){
            nsc->s = s ; /* need to keep the reference because C does not
                          * have this.
                          */
            nsc->fopen = (void *)iml_lfopen;
            nsc->fread = iml_lfread;
            nsc->fwrite = iml_lfwrite;
            nsc->getfsize = iml_lgetfsize;
            nsc->ftell = iml_lftell;
            nsc->fseek = iml_lfseek;
            nsc->fclose = iml_lfclose;
        }

        /* TODO !! Needs to be extended to NS_REMOTE 

        } else if (!strcmp(location, NS_REMOTE)){
            nsc->fopen = (void *)iml_rfopen;
            nsc->fread = iml_rfread;
            nsc->fwrite = iml_rfwrite;
            nsc->getfsize = iml_rgetfsize;
            nsc->ftell = iml_rftell;
            nsc->fseek = iml_rfseek;
            nsc->fclose = iml_rfclose;
        }
        */
        return (void *)nsc;
    }
    return (void *)NULL;
}

Public void
iml_setvalue(
    iml_session_t * s, /* in */
    char *attribute,   /* in */
    void *value        /* in */
)
{
    /* TODO !! */

    return;
}

Private char *
lookup_config()
{
    // Need to parse the XML configuration file and return the location.
    // location can be NS_LOCAL or NS_REMOTE or NS_DATABASE

    // return NS_LOCAL;
    return NS_REMOTE;
}

Private void *
iml_lfopen(
    iml_nsc *nsc,
    char *filename,
    char *mode
)
{
    char buf[PATH_MAX];

    snprintf(buf, sizeof(buf), "%s/%s/%s",LOCAL_NS_DIR, nsc->s->desktop->user_name, filename);
    secure_pathname(buf);
    return fopen(buf, mode);
}

Private size_t
iml_lfread(
    void *ptr,
    size_t size,
    size_t nitems,
    void *nsid
)
{
    return fread(ptr, size, nitems, nsid);
}

Private size_t
iml_lfwrite(
    void *ptr,
    size_t size,
    size_t nitems,
    void *nsid
)
{
    return fwrite(ptr, size, nitems, nsid);
}

Private int
iml_lgetfsize(
    void *nsid
)
{
    int fd = fileno((FILE *)nsid);
    struct stat buf;
    if (fstat(fd, &buf) < 0) {
        return -1;
    }
    return buf.st_size;
}

Private long
iml_lftell(
    void *nsid
)
{
    return ftell(nsid);
}

Private int
iml_lfseek(
    void *nsid,
    long offset,
    int whence
)
{
    return fseek(nsid, offset, whence);
}

Private int
iml_lfclose(
    iml_nsc *nsc,
    void *nsid
)
{
    if (nsc) free(nsc);
    return fclose(nsid);
}

Private void
iml_ns_read_ahead(
    iml_session_t * s, /* in */
    int ulid,          /* in */
    void *value        /* in */
){
    IMReadNSStruct *read_ns;
    iml_inst *lp;
    iml_nsc *nsc = (iml_nsc *)value ;

    lp = (iml_inst *) s->If->m->iml_new(s, sizeof(iml_inst) + sizeof(IMReadNSStruct));

    lp->opcode = IMM_READ_NS; lp->next = NULL; 
    read_ns = (IMReadNSStruct *) & (lp->operand); 
    read_ns->self = read_ns ; /* paranoia */
    read_ns->listener_id = ulid;
    read_ns->filename = secure_pathname(nsc->filename);
    (iml_inst *) s->If->m->iml_execute(s, &lp);
    (iml_inst *) s->If->m->iml_delete(s);

}

Public int
iml_add_listener(
    iml_session_t * s,  /* wanna be iml_desktop_t * 
			 * Since iml_desktop_t only carry session_list,
			 * it would be difficult to find the right session
			 * to execute as surrogate.....
			 */
    char *attribute,    /* in */
    void *listener,     /* in */
    void *value         /* in */
)
{
    char* hotkeyconf;
    int unique_listener_id = 0;

    hotkeyconf = lookup_config();
    if (!strcmp(hotkeyconf, NS_REMOTE)) {	
	if (!strcmp("namespace_context_retrieval", attribute)){
	    unique_listener_id = s->desktop->addListenerToDesktop (s, listener, value);
	    iml_ns_read_ahead(s, unique_listener_id, value); 
	}
    }
    return unique_listener_id;
}

Public int
iml_delete_listener(
    iml_session_t * s,    /* in */
    int listener_id       /* in */
)
{
    return listener_id;
}

Private char*
secure_pathname(
    char *p
)
{
    char *q ;
    int index = 0;

    while (*p) {
	if ((q = (strstr(p, "../")))) {
	    index = p - q;
	    if (index < 0)
		index = -index;
	    strcpy(p+index, p+index+3);
	} else if ((q = (strstr(p, "//")))) {
	    index = p - q;
	    if (index < 0)
		index = -index;
	    strcpy(p+index, p+index+1);
	} else if ((q = (strstr(p, "./")))) {
	    index = p - q;
	    if (index < 0)
		index = -index;
	    strcpy(p+index, p+index+2);
	} else {
	    break ;
	}
    }
    return p;
}

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