/*  esecannaserver --- pseudo canna server that wraps another IME.
 *  Copyright (C) 1999-2000 Yasuhiro Take
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  ree Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
#include <sys/types.h>

#include "def.h"
#include "misc.h"
#include "vje.h"
#include "vjesocket.h"

typedef struct _vje25_t {
  char *maindic; /* 使用するメイン辞書のパス */
  char *userdic; /* 使用するユーザー辞書のパス */
  char *optdic[8]; /* 使用するオプション辞書のパス */
  char *restart; /* vjed を再起動するコマンドへのフルパス */
  char userdic_copy_to_home:1; /* ユーザー辞書を HOME にコピーする？ */
  char katakana_gakushu:1; /* カタカナに変換したのを学習する？ */
  char restart_vjed:1; /* vjed が落ちたとき、再起動する？ 終了する？ */
} vje25_t;

typedef struct {
  char romaji[5];
  char yomi[5];
} roma_t;

typedef struct _context_t {
  struct _context_t *prev, *next;
  short context_num;

  short max_bun;
  struct {
    uchar yomi_pnt;
    uchar yomi_len;
    ushort koho_num;
    ushort *koho;
    char *koho_inf;
    size_t koho_len;
  } bun[MAX_BUNSETU_NUM];
  
  int client_id; /* このコンテキストのオーナーのクライアントの id */

  int canna_mode; /* かんなの変換モード */
  
  uchar yomi_buffer[HIRA_BUF_SIZE]; /* 読みのバッファ. vje 形式 */
  char subst_yomi_zero_flag; /* subst_yomi で yomi が null だったか？ */

  struct {
    short id; /* vje library の id. */
    char sdic_opened_flag[10];
  } vje;
} context_t;

static context_t *cx_top;

static client_t *client;

#define VW_ERROR16(_buf) { \
  cannaheader_t *he; \
  he = (cannaheader_t *)(_buf); \
  he->datalen = LSBMSB16(2); \
  he->err.e16 = LSBMSB16(-1); \
}

#define VW_ERROR8(_buf) { \
  cannaheader_t *he; \
  he = (cannaheader_t *)(_buf); \
  he->datalen = LSBMSB16(1); \
  he->err.e8 = -1; \
}

/*
 * wrapper functions から使われる ユーティリティ関数s
 */

/* コンテキスト関連 */

static short vw_new_context(int id)
{
  context_t *cx, *new;
  short cx_num;
  
  if ((new = (context_t *)calloc(1, sizeof(context_t))) == NULL)
    return -1;
  
  cx = cx_top;
  if (cx) {
    while (cx->next) cx = cx->next;
    cx->next = new;
    new->prev = cx;
  } else {
    cx_top = new;
  }

  cx_num = 1;
  for (;;) {
    cx = cx_top;

    for (;;) {
      if (cx == NULL) {
	new->context_num = cx_num;
	new->client_id = id;
	new->vje.id = 0;
	return cx_num;
      }
      
      if (cx->context_num == cx_num) {
	cx_num++;
	break;
      }

      cx = cx->next;
    }
  }
}

static context_t *vw_get_context(short cx_num)
{
  context_t *cx;
  
  if (cx_num == -1)
    return NULL;

  cx = cx_top;

  while (cx) {
    if (cx->context_num == cx_num)
      return cx;
    cx = cx->next;
  }

  return NULL;
}

static int vw_clear_context(short cx_num)
{
  context_t *cx;
  int i;

  cx = vw_get_context(cx_num);

  for (i = 0; i < cx->max_bun; i++) {
    if (cx->bun[i].koho)
      free(cx->bun[i].koho);
    cx->bun[i].koho = NULL;
    if (cx->bun[i].koho_inf)
      free(cx->bun[i].koho_inf);
    cx->bun[i].koho_inf = NULL;
    cx->bun[i].yomi_pnt = cx->bun[i].yomi_len = 0;
    cx->bun[i].koho_num = cx->bun[i].koho_len = 0;
  }
  cx->max_bun = 0;
  
  memset(cx->yomi_buffer, 0, HIRA_BUF_SIZE);

  return 0;
}

static int vw_free_context(short cx_num)
{
  context_t *cx;

  cx = vw_get_context(cx_num);
  vw_clear_context(cx_num);
  
  if (cx->prev)
    cx->prev->next = cx->next;
  else
    cx_top = cx->next;
  if (cx->next)
    cx->next->prev = cx->prev;

  free(cx);

  return 0;
}

/* VJELIB 関係 */

static int vw_open_vje_library(short cx_num)
{
  short vje_id;
  context_t *cx;
  int id, i, j, mode;
  char *dic;

  cx = vw_get_context(cx_num);
  id = cx->client_id;

  if ((vje_id = vje_proto_vjelibopen()) != -1) {
    
    cx->vje.id = vje_id;

    vje_proto_loadcoderange();

    for (i = 0; i < 10; i++) {
      switch(i) {
	case 0: dic = client[id].data.vje25->maindic; j = 38; break;
	case 9: dic = client[id].data.vje25->userdic; j = 39; break;
	default: dic = client[id].data.vje25->optdic[i - 1]; j = 38; break;
      }

      if (dic) {
	vje_proto_clear(vje_id);
	vje_proto_open_sdic(vje_id, i, dic, j);
	cx->vje.sdic_opened_flag[i] = 1;
      }
    }

    mode = 0x5700;
    mode |= client[id].data.vje25->katakana_gakushu ? 0x40 : 0;

    vje_proto_setmode(vje_id, mode, 0x300, 10, 3);

    /* OpenKanjiTable, VRoomajiOpen */
    
    vje_proto_setmode(vje_id, mode, 0x500, 10, 3);

    return 0;
  }

  return -1;
}

static int vw_close_vje_library(short cx_num)
{
  int i;
  context_t *cx;

  cx = vw_get_context(cx_num);

  if (cx->vje.id) {
    for (i = 0; i < 10; i++)
      if (cx->vje.sdic_opened_flag[i])
	vje_proto_close_sdic(cx->vje.id, i);
    
    vje_proto_vjelibclose(cx->vje.id);
    cx->vje.id = 0;
  }
    
  return 0;
}

/* ローマ字テーブル関係 */

roma_t romatbl[] = {
  {"a", "あ"},
  {"i", "い"},
  {"ye", "いぇ"},
  {"u", "う"},
  {"e", "え"},
  {"o", "お"},
  {"ka", "か"},
  {"ki", "き"},
  {"ku", "く"},
  {"ke", "け"},
  {"ko", "こ"},
  {"ga", "が"},
  {"gi", "ぎ"},
  {"gu", "ぐ"},
  {"ge", "げ"},
  {"go", "ご"},
  {"kya", "きゃ"},
  {"kyi", "きぃ"},
  {"kyu", "きゅ"},
  {"kye", "きぇ"},
  {"kyo", "きょ"},
  {"gya", "ぎゃ"},
  {"gyi", "ぎぃ"},
  {"gyu", "ぎゅ"},
  {"gye", "ぎぇ"},
  {"gyo", "ぎょ"},
  {"gwa", "ぐぁ"},
  {"gwi", "ぐぃ"},
  {"gwu", "ぐぅ"},
  {"gwe", "ぐぇ"},
  {"gwo", "ぐぉ"},
  {"sa", "さ"},
  {"si", "し"},
  {"su", "す"},
  {"se", "せ"},
  {"so", "そ"},
  {"za", "ざ"},
  {"zi", "じ"},
  {"zu", "ず"},
  {"ze", "ぜ"},
  {"zo", "ぞ"},
  {"sha", "しゃ"},
  {"syi", "しぃ"},
  {"shu", "しゅ"},
  {"she", "しぇ"},
  {"sho", "しょ"},
  {"ja", "じゃ"},
  {"jyi", "じぃ"},
  {"ju", "じゅ"},
  {"je", "じぇ"},
  {"jo", "じょ"},
  {"ta", "た"},
  {"ti", "ち"},
  {"tu", "つ"},
  {"te", "て"},
  {"to", "と"},
  {"da", "だ"},
  {"di", "ぢ"},
  {"du", "づ"},
  {"de", "で"},
  {"do", "ど"},
  {"cha", "ちゃ"},
  {"cyi", "ちぃ"},
  {"chu", "ちゅ"},
  {"che", "ちぇ"},
  {"cho", "ちょ"},
  {"tsa", "つぁ"},
  {"tsi", "つぃ"},
  {"tse", "つぇ"},
  {"tso", "つぉ"},
  {"tha", "てゃ"},
  {"thi", "てぃ"},
  {"thu", "てゅ"},
  {"the", "てぇ"},
  {"tho", "てょ"},
  {"dya", "ぢゃ"},
  {"dyi", "ぢぃ"},
  {"dyu", "ぢゅ"},
  {"dye", "ぢぇ"},
  {"dyo", "ぢょ"},
  {"dha", "でゃ"},
  {"dhi", "でぃ"},
  {"dhu", "でゅ"},
  {"dhe", "でぇ"},
  {"dho", "でょ"},
  {"na", "な"},
  {"ni", "に"},
  {"nu", "ぬ"},
  {"ne", "ね"},
  {"no", "の"},
  {"nn", "ん"},
  {"nya", "にゃ"},
  {"nyi", "にぃ"},
  {"nyu", "にゅ"},
  {"nye", "にぇ"},
  {"nyo", "にょ"},
  {"ha", "は"},
  {"hi", "ひ"},
  {"fu", "ふ"},
  {"he", "へ"},
  {"ho", "ほ"},
  {"ba", "ば"},
  {"bi", "び"},
  {"bu", "ぶ"},
  {"be", "べ"},
  {"bo", "ぼ"},
  {"pa", "ぱ"},
  {"pi", "ぴ"},
  {"pu", "ぷ"},
  {"pe", "ぺ"},
  {"po", "ぽ"},
  {"hya", "ひゃ"},
  {"hyi", "ひぃ"},
  {"hyu", "ひゅ"},
  {"hye", "ひぇ"},
  {"hyo", "ひょ"},
  {"fa", "ふぁ"},
  {"fi", "ふぃ"},
  {"fe", "ふぇ"},
  {"fo", "ふぉ"},
  {"bya", "びゃ"},
  {"byi", "びぃ"},
  {"byu", "びゅ"},
  {"bye", "びぇ"},
  {"byo", "びょ"},
  {"pya", "ぴゃ"},
  {"pyi", "ぴぃ"},
  {"pyu", "ぴゅ"},
  {"pye", "ぴぇ"},
  {"pyo", "ぴょ"},
  {"ma", "ま"},
  {"mi", "み"}, 
  {"mu", "む"},
  {"me", "め"},
  {"mo", "も"},
  {"mya", "みゃ"},
  {"myi", "みぃ"},
  {"myu", "みゅ"},
  {"mye", "みぇ"},
  {"myo", "みょ"},
  {"ya", "や"},
  {"yu", "ゆ"},
  {"yo", "よ"},
  {"ra", "ら"},
  {"ri", "り"},
  {"ru", "る"},
  {"re", "れ"},
  {"ro", "ろ"},
  {"rya", "りゃ"},
  {"ryi", "りぃ"},
  {"ryu", "りゅ"},
  {"rye", "りぇ"},
  {"ryo", "りょ"},
  {"wa", "わ"},
  {"wi", "ゐ"},
  {"we", "ゑ"},
  {"wo", "を"},
  {"vu", "ヴ"},
  {"va", "ヴァ"},
  {"vi", "ヴィ"},
  {"ve", "ヴェ"},
  {"vo", "ヴォ"},
  {"xa", "ぁ"},
  {"xi", "ぃ"},
  {"xu", "ぅ"},
  {"xe", "ぇ"},
  {"xo", "ぉ"},
  {"xwa", "ゎ"},
  {"xtu", "っ"},
  {"xya", "ゃ"},
  {"xyu", "ゅ"},
  {"xyo", "ょ"}
};

#define ROMATBL_SIZE (sizeof(romatbl) / sizeof(roma_t))

static int vw_create_romaji_table()
{
  /* romatbl[] の各読みデータを SJIS に変換する */
  int i;
  char buf[20];
  
  for (i = 0; i < ROMATBL_SIZE; i++) {
    euc2sjis(romatbl[i].yomi, strlen(romatbl[i].yomi), buf, 20);

    strcpy(romatbl[i].yomi, buf);
  }

  return 0;
}

static int vw_lookup_romaji_table(uchar *sjisyomi, int slen)
{
  int i;
  char buf[5];

  strncpy(buf, sjisyomi, slen);
  buf[slen] = '\0';

  for (i = 0; i < ROMATBL_SIZE; i++)
    if (strcmp(romatbl[i].yomi, buf) == 0)
      return i;

  return -1;
}

/* コンフィギュレーションファイル関係 */

static int vw_read_conf_file(int id, char *conffilepath)
{
  FILE *fp;
  char buf[1024], optdic[11];
  char *ope, *val;
  int i, ret = 0;
  vje25_t *vje25;

  vje25 = client[id].data.vje25;

  if ((fp = fopen(conffilepath, "r")) == NULL) {
    m_msg("Cannot open Conffile %s.\n", conffilepath);
    return -1;
  }

  while (fgets(buf, 1024, fp)) {
    if (buf[0] != '#' && m_conf1_parse(buf, &ope, &val) == 0) {
      m_conf_string(ope, "VJE25.MainDic", val, &(vje25->maindic));
      m_conf_string(ope, "VJE25.UserDic", val, &(vje25->userdic));
      
      i = M_CONF_ONOFF(ope, "VJE25.UserDicCopy", val);
      if (i) vje25->userdic_copy_to_home = i - 1;
      
      i = M_CONF_ONOFF(ope, "VJE25.KatakanaGaku", val);
      if (i) client[id].data.vje25->katakana_gakushu = i - 1;
      
      for (i = 0; i < 8; i++) {
	sprintf(optdic, "VJE25.OptDic%d", i + 1);
	m_conf_string(ope, optdic, val, &(vje25->optdic[i]));
      }
      
      if (id == VJE_ROOT_CLIENT) {
	m_conf_string(ope, "VJE25.RestartComm", val, &(vje25->restart));
	
	i = M_CONF_ONOFF(ope, "VJE25.RestartVjed", val);
	if (i) vje25->restart_vjed = i - 1;
      }
    }
  }

  fclose(fp);

  return ret;
}

static int vw_config_client(int id, char *conffile)
{
  int user_ret;
  char *userdic, *userdicx, *userdic_dir, *userdic_file, *p;
  vje25_t *vje25;

  if ((vje25 = client[id].data.vje25 = calloc(1, sizeof(vje25_t))) == NULL) {
    m_msg("Out of Memory.\n");
    return -1;
  }

  vje25->userdic_copy_to_home = TRUE;
  
  user_ret = vw_read_conf_file(id, conffile);

  if (user_ret == -1)
    return -1;

  if (access(vje25->maindic, F_OK))
    m_msg("Maindic %s not found. WHY?\n", vje25->maindic);
  
  userdicx = NULL;

  if (vje25->userdic_copy_to_home && id != VJE_ROOT_CLIENT) {
    userdic = vje25->userdic;
    
    if (userdic && m_splitpath(userdic, &userdic_dir, &userdic_file) == 0) {
      if (userdic_dir && strcmp(userdic_dir, client[id].homedir) != 0) {
	p = m_makepath(client[id].homedir, userdic_file);

	if (p) {
	  if (access(p, R_OK)) {
	    if (m_copy_file(userdic, p) == 0)
	      userdicx = p;
	  } else
	    userdicx = p;
	}
	
	free(userdic_dir);
	free(userdic_file);
      } else {
	userdicx = userdic;
      }
    }
  } else {
    userdicx = vje25->userdic;
  }

  if (userdicx != vje25->userdic && vje25->userdic != NULL)
    free(vje25->userdic);
  
  vje25->userdic = userdicx;
  
  return 0;
}

/* vjed が落ちたときの処理に関する関数s */

static int vw_check8(buffer_t *cbuf)
{
  extern int vjeerror;
  
  if (vjeerror) {
    /* vjeproto.c で、vjed とのデータのやり取りに失敗(vjed が落ちた場合)
       すると、とりあえず、vjeerror を TRUE にする。
       vjewrapper.c 側では、vje_proto_*() を呼んだあと、vw_check*() を
       呼んで、エラーがあったかどうかチェックする。
       cannaproto.c では、vjewrapper_*() の返り値により、エラーがあったか
       どうか分かり、エラーがあったのなら「vjed 再起動処理」を行い、
       再起動に成功したのなら(すなわち再接続に成功したのなら)、
       vjewrapper_restarted() を呼ぶ。
       もし失敗したのなら、全ての vje client を close する。rootclient も
       close する。imewrapper.c で「vjed は落ちてるよ」というフラグをたて、
       以後 imewrapper_initialize() で、接続する IME が VJE であった場合に
       再び vje との接続を初期化するようにする。
       
       vjeerror が TRUE ならば、vje_proto_*() は No Operation である。
    */

    VW_ERROR8(cbuf->buf);
    
    return 1;
  }

  return 0;
}


static int vw_check16(buffer_t *cbuf)
{
  extern int vjeerror;
  
  if (vjeerror) {
    VW_ERROR16(cbuf->buf);
    
    return 1;
  }

  return 0;
}

int vjewrapper_restarted()
{
  extern int vjeerror;

  vjeerror = FALSE;

  return 0;
}

int vjewrapper_clear_vjeid()
{
  context_t *cx;

  cx = cx_top;

  while (cx) {
    if (cx->vje.id)
      cx->vje.id = 0;
    cx = cx->next;
  }

  return 0;
}
  

/* その他、vje に深く関係する関数s */

static int vw_expr_yomi_start(int start, uchar *yomibuf)
{
  int start2 = 0;
  int i = 0;

  while (yomibuf[i]) {
    if (start == start2)
      return i;

    if (ISSJISKANJI1(yomibuf[i]))
      i += 2;
    else
      i++;

    start2++;
  }

  if (start == start2)
    return i;

  return -1;
}

static int vw_make_vjebuffers(uchar *in_buf, ushort *atrbuf, uchar *keybuf)
{
  int atr_pnt, key_pnt, in_pnt, d_in_pnt;
  int i, romaslen;
  int zenkana_flag;
  uchar roma[5];
  uchar hira[5];

  in_pnt = atr_pnt = key_pnt = 1;
  d_in_pnt = 0;
  
  while (in_pnt <= LEN_OF_OLDBUF(in_buf)) {
    zenkana_flag = 0;
    if (in_pnt + 1 <= LEN_OF_OLDBUF(in_buf) &&
	(ISSJIS_ZENKAKU_HIRAGANA(in_buf[in_pnt], in_buf[in_pnt + 1]) ||
	 ISSJIS_ZENKAKU_VU(in_buf[in_pnt], in_buf[in_pnt + 1]))) {
      /* ひらがな,または「ヴ」の場合 */
      if (in_pnt + 3 <= LEN_OF_OLDBUF(in_buf) &&
	  (i = vw_lookup_romaji_table(&(in_buf[in_pnt]), 4)) != -1) {
	/* "にゃ" のような読みの場合 */
	strcpy(roma, romatbl[i].romaji);
	d_in_pnt = 4;
      } else if ((i = vw_lookup_romaji_table(&(in_buf[in_pnt]), 2)) != -1) {
	/* "に" のような読みの場合 */
	strcpy(roma, romatbl[i].romaji);
	d_in_pnt = 2;
      }
    } else if (in_pnt + 1 <= LEN_OF_OLDBUF(in_buf) &&
	       ISSJIS_ZENKAKU_KATAKANA(in_buf[in_pnt], in_buf[in_pnt + 1])) {
      zenkana_flag = 1;
      /* カタカナの場合 */
      d_in_pnt = -1;
      if (in_pnt + 3 <= LEN_OF_OLDBUF(in_buf) &&
	  ISSJIS_ZENKAKU_KATAKANA(in_buf[in_pnt + 2], in_buf[in_pnt + 3])) {
	/* 次の読みもカタカナの場合 */
	m_convert_zenkana2zenhira(&(in_buf[in_pnt]), hira, 4);

	if ((i = vw_lookup_romaji_table(hira, 4)) != -1) {
	  /* "キャ" のような読みの場合 */
	  strcpy(roma, romatbl[i].romaji);
	  d_in_pnt = 4;
	}
      }

      if (d_in_pnt == -1) {
	/* 次の読みもカタカナだが "キャ" のような読みではない場合,または
	   次の読みがカタカナではない場合 */
	m_convert_zenkana2zenhira(&(in_buf[in_pnt]), hira, 2);

	if ((i = vw_lookup_romaji_table(hira, 2)) != -1) {
	  /* "ニ" のような読みの場合 */
	  strcpy(roma, romatbl[i].romaji);
	  d_in_pnt = 2;
	}
      }

      if (d_in_pnt == -1) {
	m_msg("vw_make_vjebuffer(): unexpected error01 %d\n", in_pnt);
	return -1;
      }
    } else if (ISSJISKANJI1(in_buf[in_pnt])) {
      /* "ａ" "＠" "「" などの全角非ひらがな */
      if ((roma[0] = m_convert_zen2han(&(in_buf[in_pnt]))) == 0) {
	m_msg("make_vjebuffer(): unexpected error02 %d\n", in_pnt);
	return -1;
      }
      roma[1] = '\0';
      d_in_pnt = 2;
    } else {
      /* 半角のアルファベット,数字 */
      roma[0] = in_buf[in_pnt]; 
      roma[1] = '\0';
      d_in_pnt = 1;
    }
    
    romaslen = strlen(roma);

    switch (romaslen) {
      case 3:
	if (zenkana_flag == 0) {
	  atrbuf[atr_pnt++] = 0x83; atrbuf[atr_pnt++] = 0x40;
	  if (d_in_pnt == 4) {
	    atrbuf[atr_pnt++] = 0x80; atrbuf[atr_pnt++] = 0x40;
	  }
	} else {
	  atrbuf[atr_pnt++] = 0xab; atrbuf[atr_pnt++] = 0x68;
	  atrbuf[atr_pnt++] = 0xa8; atrbuf[atr_pnt++] = 0x68;
	}
	break;
      case 2:
	if (zenkana_flag == 0) {
	  atrbuf[atr_pnt++] = 0x82; atrbuf[atr_pnt++] = 0x40;
	  if (d_in_pnt == 4) {
	    atrbuf[atr_pnt++] = 0x80; atrbuf[atr_pnt++] = 0x40;
	  }
	} else {
	  atrbuf[atr_pnt++] = 0xaa; atrbuf[atr_pnt++] = 0x68;
	  if (d_in_pnt == 4) {
	    atrbuf[atr_pnt++] = 0xa8; atrbuf[atr_pnt++] = 0x68;
	  }
	}
	break;
      case 1:
	if (d_in_pnt == 2) {
	  if (zenkana_flag) {
	    atrbuf[atr_pnt++] = 0xa9; atrbuf[atr_pnt++] = 0x68;
	  } else if (roma[0] >= 0xa1 && roma[0] <= 0xdf) {
	    atrbuf[atr_pnt++] = 0x8081; atrbuf[atr_pnt++] = 0x8040;
	  } else {
	    atrbuf[atr_pnt++] = 0x81; atrbuf[atr_pnt++] = 0x40;
	  }
	} else
	  atrbuf[atr_pnt++] = 0x01;
	break;
    }

    memcpy(&(keybuf[key_pnt]), roma, romaslen);
    key_pnt += romaslen;
    in_pnt += d_in_pnt;
  }

  LEN_OF_OLDBUF(atrbuf) = atr_pnt - 1;
  LEN_OF_OLDBUF(keybuf) = key_pnt - 1;

  return 0;
}

static int vw_move_bunsetu(context_t *cx, int n)
{
  /*
  while (cx->vje.cur_bun_no < n) {
    vje_proto_ji_bunsetu(vjesockfd, cx->vje.id);
    cx->vje.cur_bun_no++;
  }

  while (cx->vje.cur_bun_no > n) {
    vje_proto_zen_bunsetu(vjesockfd, cx->vje.id);
    cx->vje.cur_bun_no--;
  }
  */

  vje_proto_set_koho_bno(cx->vje.id, n);
  
  return 0;
}

static int vw_convert_yomi_canna2vje(uchar *euc, int slen)
{
  euc[slen] = 0;

  m_replace_string(euc, "″", "”");
  slen = m_replace_string(euc, "う゛", "ヴ");
  
  return slen;
}

static int vw_convert_yomi_vje2canna(uchar *euc, int slen)
{
  euc[slen] = 0;
  
  m_replace_string(euc, "”", "″");
  slen = m_replace_string(euc, "ヴ", "う゛");

  return slen;
}

static int vw_get_yomi(context_t *cx, int bun_no, uchar *euc)
{
  uchar tmpbuf[IN_BUF_SIZE];
  int slen;
  
  slen = cx->bun[bun_no].yomi_len;

  memcpy(tmpbuf, &(cx->yomi_buffer[cx->bun[bun_no].yomi_pnt]), slen);
  tmpbuf[slen] = 0;

  slen = sjis2euc(tmpbuf, slen, euc, IN_BUF_SIZE);

  slen = vw_convert_yomi_vje2canna(euc, slen);

  euc[slen] = 0;
  
  return slen;
}

static ushort *vw_get_koho_list(context_t *cx, int bun_no, int *koho_num_r,
				int *size_r, char **koho_inf_r)
{
  int block, i, j, k, pnt, buf_pnt, slen, slen2, koho_num, jiblock_ret;
  uchar sjis[IN_BUF_SIZE], euc[IN_BUF_SIZE], tmpbuf[IN_BUF_SIZE];
  uchar dic_buf[DIC_BUF_SIZE * 4];
  ushort wcs[IN_BUF_SIZE];
  short size;
  char *koho_inf;
  uchar yomi_len;
  int hira_block, hira_koho, kata_block, kata_koho;
  
  extern int vjeerror;
  
  buffer_t zbuf, zbuf2;
  doubufx_t douon;
  buninf25_t bi;

  zbuf.size = zbuf2.size = 0;
  zbuf.buf = zbuf2.buf = NULL;
  hira_block = hira_koho = kata_block = kata_koho = -1;
  
  vw_move_bunsetu(cx, bun_no);
  
  vje_proto25_get_koho_all(cx->vje.id, &bi, tmpbuf);
  yomi_len = bi.kouho[bun_no].yomi_len;

  memset(dic_buf, 0, DIC_BUF_SIZE * 4);
  
  block = koho_num = size = buf_pnt = 0;
  jiblock_ret = -1;
  for (;;) {
    vje_proto_get_douon(cx->vje.id, &douon);

    if (vjeerror) {
      if (zbuf.buf)
	free(zbuf.buf);
      if (zbuf2.buf)
	free(zbuf2.buf);

      *koho_num_r = 0;
      *size_r = 0;
      *koho_inf_r = NULL;
      
      return NULL;
    }

    if (jiblock_ret == 0 && memcmp(douon.buf, dic_buf, douon.len) == 0) {
      block--;
      break;
    }
    
    pnt = 0;
    for (i = 0; i < douon.cnt; i++) {
      slen = douon.buf[pnt++];
      memcpy(sjis, &(douon.buf[pnt]), slen);
      sjis[slen] = 0;
      pnt += slen;

      slen = sjis2euc(sjis, slen, euc, IN_BUF_SIZE);

      if (m_exist_hankata(euc) == 0) {
	if (m_is_zenkata_string(euc)) {
	  kata_block = block;
	  kata_koho = i;
	} else if (m_is_hiragana_string(euc)) {
	  hira_block = block;
	  hira_koho = i;
	}

	slen = euc2cannawc(euc, slen, wcs, IN_BUF_SIZE);
	slen *= 2;
	
	size += slen + 2;
	
	buffer_check(&zbuf, size);
	
	memcpy(&(zbuf.buf[buf_pnt]), wcs, slen);
	buf_pnt += slen;
	zbuf.buf[buf_pnt++] = 0; zbuf.buf[buf_pnt++] = 0;
	
	koho_num++;
	
	buffer_check(&zbuf2, koho_num);
	koho_inf = (char *)zbuf2.buf;
	koho_inf[koho_num - 1] = block;
      }
    }

    if ((jiblock_ret = vje_proto_ji_block(cx->vje.id)) == 0) {
      /* ji_block の返り値が 0 のとき、これ以上次ブロックがない可能性がある。
	 その場合、douon.buf の内容は変わらないので、そのチェックのため、
	 douon.buf のバックアップをとる。*/
      memcpy(dic_buf, douon.buf, douon.len);
    }
    block++;

    vje_proto25_get_koho_all(cx->vje.id, &bi, tmpbuf);
    if (yomi_len != bi.kouho[bun_no].yomi_len) {
      /* ji_block により、文節の区切りが短くなった場合、おしまい */
      break;
    }
  }

  for (i = 0; i < block; i++)
    vje_proto_zen_block(cx->vje.id);

  vje_proto25_get_koho_all(cx->vje.id, &bi, tmpbuf);
  if (yomi_len != bi.kouho[bun_no].yomi_len) {
    /* ブロックを戻しすぎた場合 */
    vje_proto_ji_block(cx->vje.id);
  }

  slen = vw_get_yomi(cx, bun_no, euc);

  /* cx->canna_mode に従い、候補を付け足す */

  j = m_count_canna_mode(cx->canna_mode);
  
  for (i = 0; i < j; i++) {
    k = m_get_canna_mode(cx->canna_mode, i);
    
    if (k == 3) {
      /* カタカナ候補 */
      koho_num++;

      slen2 = m_convert_zenhira2zenkata(euc, slen, tmpbuf);

      slen2 = euc2cannawc(tmpbuf, slen2, wcs, IN_BUF_SIZE);
      slen2 *= 2;
      
      size += slen2 + 2;
      
      buffer_check(&zbuf, size);
      
      memcpy(&(zbuf.buf[buf_pnt]), wcs, slen2);
      buf_pnt += slen2;
      zbuf.buf[buf_pnt++] = 0; zbuf.buf[buf_pnt++] = 0;
      
      buffer_check(&zbuf2, koho_num);
      koho_inf = (char *)zbuf2.buf;
      koho_inf[koho_num - 1] = kata_block;
    } else if (k == 1) {
      /* ひらがな候補 */
      koho_num++;
      
      slen2 = euc2cannawc(euc, slen, wcs, IN_BUF_SIZE);
      slen2 *= 2;
      
      size += slen2 + 2;
      
      buffer_check(&zbuf, size);
      
      memcpy(&(zbuf.buf[buf_pnt]), wcs, slen2);
      buf_pnt += slen2;
      zbuf.buf[buf_pnt++] = 0; zbuf.buf[buf_pnt++] = 0;

      buffer_check(&zbuf2, koho_num);
      koho_inf = (char *)zbuf2.buf;
      koho_inf[koho_num - 1] = hira_block;
    }
  }

  size += 2;
  buffer_check(&zbuf, size);
  
  zbuf.buf[buf_pnt++] = 0; zbuf.buf[buf_pnt++] = 0;

  koho_inf = (char *)zbuf2.buf;

  *koho_num_r = koho_num;
  *size_r = size;
  *koho_inf_r = koho_inf;
  
  return (ushort *)zbuf.buf;
}

static ushort *vw_retrieve_koho(short cx_num, int bun_no, int koho_no)
{
  context_t *cx;
  ushort *p, *ret;
  uchar sjis[IN_BUF_SIZE], euc[IN_BUF_SIZE];
  static ushort wcs[IN_BUF_SIZE];
  int i, j, slen, koho_num, koholen;
  char *koho_inf;
  buninf25_t bi;

  cx = vw_get_context(cx_num);
  p = cx->bun[bun_no].koho;

  if (p == NULL && koho_no > 0) {
    /* まだ候補を取得してないなら取得する */
    cx->bun[bun_no].koho = vw_get_koho_list(cx, bun_no, &koho_num, &koholen,
					    &koho_inf);
    cx->bun[bun_no].koho_inf = koho_inf;
    cx->bun[bun_no].koho_num = koho_num;
    cx->bun[bun_no].koho_len = koholen;

    p = cx->bun[bun_no].koho;
  }
  
  if (p) {
    i = 0; j = 0;
    while (i < koho_no) {
      while (p[j]) j++;
      j++;
      i++;
    }

    ret = &p[j];
  } else {
    vje_proto25_get_koho_all(cx->vje.id, &bi, sjis);
    slen = sjis2euc(&(sjis[bi.kouho[bun_no].hyk_pnt]),
		    bi.kouho[bun_no].hyk_len, euc, IN_BUF_SIZE);
    euc2cannawc(euc, slen, wcs, IN_BUF_SIZE);

    ret = wcs;
  }
  
  return ret;
}

static int vw_set_koho(short cx_num, int bun_no, int koho_no)
{
  int block, i, len, pnt;
  context_t *cx;
  uchar hira[IN_BUF_SIZE], kata[IN_BUF_SIZE], sjis[IN_BUF_SIZE];
  uchar in_buf[IN_BUF_SIZE], keybuf[IN_BUF_SIZE], tmpbuf[IN_BUF_SIZE];
  ushort atrbuf[IN_BUF_SIZE * 2], *sp;
  doubufx_t douon;

  cx = vw_get_context(cx_num);
  
  if (cx->bun[bun_no].koho_inf) {
    block = cx->bun[bun_no].koho_inf[koho_no];
    
    if (block != -1) {
      sp = vw_retrieve_koho(cx_num, bun_no, koho_no);
      len = cannawc2euc(sp, cannawcstrlen(sp), tmpbuf, IN_BUF_SIZE);
      euc2sjis(tmpbuf, strlen(tmpbuf), sjis, IN_BUF_SIZE);

      for (i = 0; i < block; i++)
	vje_proto_ji_block(cx->vje.id);
      
      vje_proto_get_douon(cx->vje.id, &douon);

      pnt = 0;
      for (i = 0; i < douon.cnt; i++) {
	len = douon.buf[pnt++];
	memcpy(tmpbuf, &(douon.buf[pnt]), len);
	tmpbuf[len] = 0;
	pnt += len;
	
	if (strcmp(tmpbuf, sjis) == 0) {
	  sjis2euc(sjis, strlen(sjis), tmpbuf, IN_BUF_SIZE);
	  
	  vje_proto_set_koho(cx->vje.id, 1 + i);

	  return 0;
	}
      }
    } else {
      /* block == -1 の時は、カタカナ候補 */
      len = vw_get_yomi(cx, bun_no, hira);
      
      m_convert_zenhira2zenkata(hira, len, kata);
      
      len = euc2sjis(kata, len, sjis, IN_BUF_SIZE);
      
      memcpy(in_buf, cx->yomi_buffer, LEN_OF_OLDBUF(cx->yomi_buffer) + 1);
      memcpy(&(in_buf[cx->bun[bun_no].yomi_pnt]), sjis, len);
      
      vw_make_vjebuffers(in_buf, atrbuf, keybuf);
      
      vje_proto25_set_kihonbuff(cx->vje.id, in_buf, atrbuf, keybuf);
      vje_proto25_saihenkan(cx->vje.id, in_buf, 1, len, len, 8);

      memcpy(cx->yomi_buffer, in_buf, LEN_OF_OLDBUF(in_buf) + 1);
    }
  } else {
    /* koho_inf == NULL ということは、GetCandidacyList を呼んでいない。
       この場合、候補 No. は 0 ,すなわち最優先候補に決まってるはず。
    */
       
    vje_proto_set_koho(cx->vje.id, 1);
  }
  
  return 0;
}

/*
 * vw_after_henkan() : 変換後呼ぶ関数
 *                   - in_buf : 読み
 *                   - bun_no : 各文節の最優先候補のうち、bun_no で指定された
 *                            : 文節からの候補s を wcs_r にいれる。
 *                   - 帰り値 : wcs_r の長さ
 */

static int vw_after_henkan(short cx_num, uchar *in_buf, ushort *wcs_r,
			   int bun_no)
{
  int i, pnt, len;
  uchar bufsjis[IN_BUF_SIZE], bufeuc[IN_BUF_SIZE];
  buninf25_t bi;
  context_t *cx;

  cx = vw_get_context(cx_num);

  vje_proto25_get_koho_all(cx->vje.id, &bi, bufsjis);

  sjis2euc(bufsjis + 1, bufsjis[0], bufeuc, IN_BUF_SIZE);

  m_msg_dbg("kouho = ");
  
  pnt = 0;
  for (i = bun_no; i < bi.max_bun; i++) {
    len = sjis2euc(&(bufsjis[bi.kouho[i].hyk_pnt]),bi.kouho[i].hyk_len,
		   bufeuc, IN_BUF_SIZE);
    m_msg_dbg("%d:[%s] ", i, bufeuc);
    len = m_convert_hankana2zenkana(bufeuc, len);
    len = euc2cannawc(bufeuc, len, &(wcs_r[pnt]), IN_BUF_SIZE - pnt);
    pnt += len;
    wcs_r[pnt++] = 0;
  }

  m_msg_dbg("\n");
  
  wcs_r[pnt++] = 0;
  
  vw_clear_context(cx_num);

  memcpy(cx->yomi_buffer, in_buf, IN_BUF_SIZE);

  cx->max_bun = bi.max_bun;
  
  for (i = cx->max_bun - 1; i >= 0; i--) { /* ???: 意味不明。
					      i = 0 -> cx->max_bun - 1 では
					      (文節縮めで)増えた文節の候補を
					      取得できない
					   */
    cx->bun[i].yomi_pnt = bi.kouho[i].yomi_pnt;
    cx->bun[i].yomi_len = bi.kouho[i].yomi_len;
  }
  
  return pnt * 2;
}

/*
 * 外に公開する関数s 初期化終了関係
 */

/* dlopen() されたあと, client[] を vjewrapper.c に渡すために呼ばれる */

int vjewrapper_dl_started(client_t *cl)
{
  client = cl;

  m_msg("Module: %s\n", ESECANNA_MODULE_VERSION);

  return 0;
}

int vjewrapper_init_rootclient()
{
  char buf[128];
  short cx_num;

  m_msg("Initializing root client for VJE25.\n");
  
  if (vw_config_client(VJE_ROOT_CLIENT, ESECANNA_RC_PATH) == -1) {
    m_msg("No conffile found. Aborting.\n");
    return -1;
  }

  vw_create_romaji_table();
  
  if (gethostname(buf, 128))
    strcpy(buf, "localhost");

  if (vje_proto_set_clienthostname(buf) == -1) {
    m_msg("set_clienthostname failed. Aborting.\n");
    return -1;
  }

  if ((cx_num = vw_new_context(VJE_ROOT_CLIENT)) == -1) {
    m_msg("Out of memory. Cannot allocate context. Aborting.\n");
    return -1;
  }

  if (vw_open_vje_library(cx_num) == -1) {
    m_msg("vjelibopen failed. Aborting.\n");
    return -1;
  }

  /* OpenKanjiTable, VRoomajiOpen */

  m_msg("Initialize succeeded.\n");
  
  return 0;
}

int vjewrapper_end_client(int id)
{
  context_t *cx, *cx2;
  
  cx = cx_top;

  while (cx) {
    if (cx->client_id == id) {
      cx2 = cx->next;

      vw_close_vje_library(cx->context_num);
      vw_free_context(cx->context_num);

      cx = cx2;
    } else
      cx = cx->next;
  }

  return 0;
}

int vjewrapper_end_rootclient()
{
  vjewrapper_end_client(VJE_ROOT_CLIENT);
  vje_proto_finish_session();

  vje_socket_close_connection();

  return 0;
}

int vjewrapper_clear_client_data(int id)
{
  int i;
  vje25_t *vje25 = client[id].data.vje25;

  MYFREE(vje25->maindic);
  MYFREE(vje25->userdic);

  for (i = 0; i < 8; i++)
    MYFREE(vje25->optdic[i]);

  MYFREE(vje25->restart);

  MYFREE(client[id].data.vje25);

  return 0;
}

/*
 * 「かんな」を vje で wrapping する関数s
 */

int vjewrapper_initialize(int id, char *conffile)
{
  if (vw_config_client(id, conffile) == 0)
    return vw_new_context(id);

  return -1;
}

int vjewrapper_finalize(int id, buffer_t *cbuf)
{
  cannaheader_t *header;
  
  client[id].need_terminate = TRUE; /* main.c で終了処理をしてもらう */
    
  header = (cannaheader_t *)cbuf->buf;
  header->type = 0x02;
  header->extra = 0;
  header->datalen = LSBMSB16(1);
  header->err.e8 = 0;

  return 1;
}

int vjewrapper_create_context(int id, buffer_t *cbuf)
{
  cannaheader_t *header = (cannaheader_t *)cbuf->buf;
  short *sp = (short *)cbuf->buf;
  short cx_num;

  cx_num = vw_new_context(id);
  
  header->type = 0x03;
  header->extra = 0;
  header->datalen = LSBMSB16(2);
  
  sp[2] = LSBMSB16(cx_num);

  return 1;
}

int vjewrapper_duplicate_context(int id, buffer_t *cbuf)
{
  cannaheader_t *header = (cannaheader_t *)cbuf->buf;
  short *sp = (short *)cbuf->buf;
  short cx_n_new, cx_n_orig;
  context_t *cx_orig, *cx_new;

  cx_n_orig = LSBMSB16(sp[2]);
  cx_n_new = vw_new_context(id);

  cx_orig = vw_get_context(cx_n_orig);
  cx_new = vw_get_context(cx_n_new);

  memcpy(cx_new->yomi_buffer, cx_orig->yomi_buffer, HIRA_BUF_SIZE);
  
  header->type = 0x04;
  header->extra = 0;
  header->datalen = LSBMSB16(2);
  
  sp[2] = LSBMSB16(cx_n_new);

  return 1;
}

int vjewrapper_close_context(int id, buffer_t *cbuf)
{
  cannaheader_t *header = (cannaheader_t *)cbuf->buf;
  short *sp = (short *)cbuf->buf;
  short cx_num;
  uchar err;

  cx_num = LSBMSB16(sp[2]);

  vw_close_vje_library(cx_num);
  err = vw_free_context(cx_num);
  
  header->type = 0x05;
  header->extra = 0;
  header->datalen = LSBMSB16(1);
  header->err.e8 = err;
  
  return 1;
}



int vjewrapper_define_word(int id, buffer_t *cbuf)
{
  VW_ERROR8(cbuf->buf);
  
  return 0;
}

int vjewrapper_delete_word(int id, buffer_t *cbuf)
{
  VW_ERROR8(cbuf->buf);
  
  return 0;
}

int vjewrapper_begin_convert(int id, buffer_t *cbuf)
{
  cannaheader_t *header = (cannaheader_t *)cbuf->buf;
  ushort *sp = (ushort *)cbuf->buf;
  int *ip = (int *)cbuf->buf;
  int len, datalen, koho_len, cmode;
  short cx_num;
  context_t *cx;
  
  short vje_id, mode = 3;

  uchar bufeuc[IN_BUF_SIZE];
  uchar in_buf[IN_BUF_SIZE], keybuf[IN_BUF_SIZE];
  ushort atrbuf[IN_BUF_SIZE * 2], wcs[IN_BUF_SIZE * 2];

  cx_num = LSBMSB16(sp[4]);
  cmode = LSBMSB32(ip[1]);

  cx = vw_get_context(cx_num);

  if (cx->vje.id == 0)
    vw_open_vje_library(cx_num); /* 失敗したら cx->vje.id は 0 のまま */

  if (vw_check16(cbuf)) return -1;

  len = cannawcstrlen((ushort *)&(cbuf->buf[10]));
  len = cannawc2euc((ushort *)&(cbuf->buf[10]), len, bufeuc, IN_BUF_SIZE);
  
  len = vw_convert_yomi_canna2vje(bufeuc, len);

  len = euc2sjis(bufeuc, len, in_buf + 1, IN_BUF_SIZE - 1);
  LEN_OF_OLDBUF(in_buf) = len;

  if (cx->vje.id != 0 && vw_make_vjebuffers(in_buf, atrbuf, keybuf) == 0) {
    vje_id = cx->vje.id;

    vje_proto_clear(vje_id);
    vje_proto_chg_sdic(vje_id, -1);
    vje_proto_set_koho_bno(vje_id, 0);
    vje_proto25_set_kihonbuff(vje_id, in_buf, atrbuf, keybuf);
    vje_proto25_henkanb(vje_id, in_buf, atrbuf, keybuf, &mode, 0);
    
    if (vw_check16(cbuf)) return -1;

    koho_len = vw_after_henkan(cx_num, in_buf, wcs, 0);

    if (vw_check16(cbuf)) return -1;

    cx->canna_mode = cmode;
    
    datalen = koho_len + 2;

    buffer_check(cbuf, 4 + datalen);
    header = (cannaheader_t *)cbuf->buf;
    sp = (ushort *)cbuf->buf;
    
    header->type = 0x0f;
    header->extra = 0;
    header->datalen = LSBMSB16(datalen);
    sp[2] = LSBMSB16(cx->max_bun);
    memcpy(&(sp[3]), wcs, koho_len);
  } else {
    header->type = 0x0f;
    header->extra = 0;
    header->datalen = LSBMSB16(2);
    header->err.e16 = LSBMSB16(-1);
  }

  return 1;
}

int vjewrapper_end_convert(int id, buffer_t *cbuf)
{
  cannaheader_t *header = (cannaheader_t *)cbuf->buf;
  short *sp = (short *)cbuf->buf;
  int *ip = (int *)cbuf->buf;
  int i, koho_no;
  short cx_num;
  char err;
  context_t *cx;
  
  char tmpbuf[IN_BUF_SIZE];

  cx_num = LSBMSB16(sp[2]);
  cx = vw_get_context(cx_num);

  err = (cx->vje.id) ? 0 : -1;
  
  if (ip[2] != 0 && err == 0) {
    for (i = 0; i < cx->max_bun; i++) {
      vw_move_bunsetu(cx, i);
      
      koho_no = LSBMSB16(sp[6 + i]);

      vw_set_koho(cx_num, i, koho_no);
    }

    if (vw_check8(cbuf)) return -1;
    
    i = vje_proto25_kakutei1(cx->vje.id, cx->yomi_buffer, tmpbuf);

    if (vw_check8(cbuf)) return -1;
  }

  vw_clear_context(cx_num);

  header->type = 0x10;
  header->extra = 0;
  header->datalen = LSBMSB16(1);
  header->err.e8 = err;
  
  return 1;
}

int vjewrapper_get_candidacy_list(int id, buffer_t *cbuf)
{
  context_t *cx;
  cannaheader_t *header;
  short *sp = (short *)cbuf->buf;
  int bun_no, koho_num, koholen;
  char *koho_inf;
  short size, cx_num;
  
  cx_num = LSBMSB16(sp[2]);
  bun_no = LSBMSB16(sp[3]);

  cx = vw_get_context(cx_num);

  if (cx->vje.id == 0) {
    VW_ERROR16(cbuf->buf);
    return 1;
  }

  /* KOHO */

  if (cx->bun[bun_no].koho == NULL) {
    cx->bun[bun_no].koho = vw_get_koho_list(cx, bun_no, &koho_num, &koholen,
					    &koho_inf);
    cx->bun[bun_no].koho_inf = koho_inf;
    cx->bun[bun_no].koho_num = koho_num;
    cx->bun[bun_no].koho_len = koholen;
  } else {
    koho_num = cx->bun[bun_no].koho_num;
    koholen = cx->bun[bun_no].koho_len;
  }

  size = koholen;
  
  if (vw_check16(cbuf)) return -1;
  
  buffer_check(cbuf, size + 6);
  sp = (short *)cbuf->buf;
  header = (cannaheader_t *)cbuf->buf;
  
  memcpy(&(cbuf->buf[6]), cx->bun[bun_no].koho, size);
  
  sp[2] = LSBMSB16(koho_num);

  size += 2;
  
  header->type = 0x11;
  header->extra = 0;
  header->datalen = LSBMSB16(size);
  
  return 1;
}

int vjewrapper_get_yomi(int id, buffer_t *cbuf)
{
  cannaheader_t *header;
  short *sp = (short *)cbuf->buf;
  int size, bun_no, slen;
  uchar euc[YOMI_BUF_SIZE];
  ushort wcs[YOMI_BUF_SIZE];
  short cx_num, si;
  context_t *cx;

  cx_num = LSBMSB16(sp[2]);
  bun_no = LSBMSB16(sp[3]);

  cx = vw_get_context(cx_num);

  slen = vw_get_yomi(cx, bun_no, euc);
  slen = euc2cannawc(euc, slen, wcs, YOMI_BUF_SIZE);
  slen *= 2;

  size = 2 + slen + 2;

  buffer_check(cbuf, size + 4);
  sp = (short *)cbuf->buf;
  header = (cannaheader_t *)cbuf->buf;

  memcpy(&(cbuf->buf[6]), wcs, slen);
  cbuf->buf[6 + slen] = 0; cbuf->buf[6 + slen + 1] = 0;
  
  header->type = 0x12;
  header->extra = 0;
  header->datalen = LSBMSB16(size);

  si = slen;
  sp[2] = LSBMSB16(si);

  return 1;
}

int vjewrapper_subst_yomi(int id, buffer_t *cbuf)
{
  cannaheader_t *header = (cannaheader_t *)cbuf->buf;
  ushort *sp = (ushort *)cbuf->buf;
  short cx_num, start, end, yomilen, mode = 0;
  int newyomipnt, size, errflag = 0, koho_len, datalen, cvt, i;
  ushort *yomi;
  uchar euc[IN_BUF_SIZE], sjis[IN_BUF_SIZE], keybuf[IN_BUF_SIZE];
  uchar in_buf[IN_BUF_SIZE];
  ushort atrbuf[IN_BUF_SIZE * 2], wcs[IN_BUF_SIZE * 2];
  context_t *cx;

  cx_num = LSBMSB16(sp[2]);
  start = LSBMSB16(sp[3]);
  end = LSBMSB16(sp[4]);
  yomilen = LSBMSB16(sp[5]);
  yomi = (ushort *)&(sp[6]);

  cx = vw_get_context(cx_num);

  memset(in_buf, 0, IN_BUF_SIZE);
  memcpy(in_buf, cx->yomi_buffer, LEN_OF_OLDBUF(cx->yomi_buffer) + 1);

  /* 変換されている読みの長さを取得 */
  for (i = cvt = 0; i < cx->max_bun; i++)
    cvt += cx->bun[i].yomi_len;

  if ((newyomipnt = vw_expr_yomi_start(start, &in_buf[1 + cvt])) < 0)
    errflag = 1;

  if (errflag == 0) {
    if (yomilen > 0) {
      cx->subst_yomi_zero_flag = 0;
      
      if (in_buf[1 + cvt + newyomipnt]) {
	in_buf[1 + cvt + newyomipnt] = 0;
	LEN_OF_OLDBUF(in_buf) = strlen(&in_buf[1]);

	vw_make_vjebuffers(in_buf, atrbuf, keybuf);
	vje_proto25_set_kihonbuff(cx->vje.id, in_buf, atrbuf, keybuf);

	/* バックスペースされたときは mode = 2 になるみたい */
	mode = 2;
      }

      cannawc2euc(yomi, yomilen, euc, IN_BUF_SIZE);
      m_msg_dbg("newyomi = %s/%d\n", euc, yomilen);
      euc2sjis(euc, strlen(euc), sjis, IN_BUF_SIZE);
      
      size = IN_BUF_SIZE - newyomipnt - cvt - 1;

      strncpy(&(in_buf[newyomipnt + cvt + 1]), sjis, size);

      in_buf[IN_BUF_SIZE - 1] = 0;
      LEN_OF_OLDBUF(in_buf) = strlen(&in_buf[1]);
    } else {
      cx->subst_yomi_zero_flag = 1;
      
      in_buf[newyomipnt + cvt + 1] = 0;
      LEN_OF_OLDBUF(in_buf) = strlen(&in_buf[1]);
    }

    sjis2euc(in_buf + 1, in_buf[0], euc, IN_BUF_SIZE);
    m_msg_dbg("yomi = [%s]/%d\n", euc, in_buf[0]);

    vw_make_vjebuffers(in_buf, atrbuf, keybuf);

    vje_proto25_set_kihonbuff(cx->vje.id, in_buf, atrbuf, keybuf);

    if (cx->subst_yomi_zero_flag == 0) {
      /* ただ単にバックスペースされた場合は, henkanb を呼ばないっぽい。*/
      vje_proto25_henkanb(cx->vje.id, in_buf, atrbuf, keybuf, &mode, cvt);
    }

    if (vw_check16(cbuf)) return -1;

    koho_len = vw_after_henkan(cx_num, in_buf, wcs, 0);

    if (vw_check16(cbuf)) return -1;

    datalen = koho_len + 2;
    
    buffer_check(cbuf, 4 + datalen);
    header = (cannaheader_t *)cbuf->buf;
    sp =(ushort *)cbuf->buf;

    header->type = 0x13;
    header->extra = 0;
    header->datalen = LSBMSB16(datalen);
    sp[2] = LSBMSB16(cx->max_bun);
    memcpy(&sp[3], wcs, koho_len);
  } else {
    header->type = 0x13;
    header->extra = 0;
    header->datalen = LSBMSB16(2);
    header->err.e16 = LSBMSB16(-1);
  }
  
  return 1;
}

int vjewrapper_store_yomi(int id, buffer_t *cbuf)
{
  cannaheader_t *header;
  ushort *sp = (ushort *)cbuf->buf;
  short cx_num, bun_no, datalen;
  ushort *yomi;
  int len, koho_len, pnt, i;
  context_t *cx;

#if 0
  buninf_t bi;
#endif
  
  uchar euc[IN_BUF_SIZE], sjis[IN_BUF_SIZE];
  uchar in_buf[IN_BUF_SIZE], keybuf[IN_BUF_SIZE];
  ushort atrbuf[IN_BUF_SIZE * 2], wcs[IN_BUF_SIZE * 2];

  datalen = LSBMSB16(sp[1]);
  cx_num = LSBMSB16(sp[2]);
  bun_no = LSBMSB16(sp[3]);

  /* かんなライブラリのバグ？ 変更後の読みをパケットに含めなあかんのに、
     変更後の読みがヌルならなにも含まれない。
     (Terminate Char である 0x0000 すら渡さない。) その対応策 */
  if (datalen > 4)
    yomi = &sp[4];
  else
    yomi = NULL;

  cx = vw_get_context(cx_num);

  if (yomi) {
    len = cannawc2euc(yomi, cannawcstrlen(yomi), euc, IN_BUF_SIZE);
    len = euc2sjis(euc, len, sjis, IN_BUF_SIZE);
  } else {
    len = 0;
  }

#if 0

  /* 厳密にはこのコードが必要かも知れないが、なくてもまともに動く？ */

  memset(in_buf, 0, IN_BUF_SIZE);

  pnt = 1;
  for (i = 0; i < cx->max_bun; i++) {
    memcpy(&in_buf[pnt], &cx->yomi_buffer[cx->bun[i].yomi_pnt],
	   cx->bun[i].yomi_len);
    pnt += cx->bun[i].yomi_len;
  }

  in_buf[pnt] = 0;
  LEN_OF_OLDBUF(in_buf) = strlen(&in_buf[1]);

  vw_make_vjebuffers(in_buf, atrbuf, keybuf);

  vje_proto_set_kihonbuff(cx->vje.id, in_buf, atrbuf, keybuf);

  vje_proto_get_koho_all(cx->vje.id, &bi, sjis);

  vje_proto_set_koho_bno(cx->vje.id, bi.max_bun - 1);
  vje_proto_chg_sdic(cx->vje.id, -1);

  vje_proto_get_koho_all(cx->vje.id, &bi, sjis);
  
#endif

  vw_move_bunsetu(cx, bun_no);

  memset(in_buf, 0, IN_BUF_SIZE);

  pnt = 1;
  for (i = 0; i < cx->max_bun; i++) {
    if (i == bun_no) {
      if (len > 0) {
	memcpy(&in_buf[pnt], sjis, len);
	pnt += len;
      }
    } else {
      memcpy(&in_buf[pnt], &cx->yomi_buffer[cx->bun[i].yomi_pnt],
	     cx->bun[i].yomi_len);
      pnt += cx->bun[i].yomi_len;
    }
  }

  in_buf[pnt] = 0;
  LEN_OF_OLDBUF(in_buf) = strlen(&in_buf[1]);

  vw_make_vjebuffers(in_buf, atrbuf, keybuf);

  vje_proto25_set_kihonbuff(cx->vje.id, in_buf, atrbuf, keybuf);

  if (len > 0)
    vje_proto25_saihenkan(cx->vje.id, in_buf, 1, len, len, 5);
  else
    vje_proto25_saihenkan(cx->vje.id, in_buf, 1, 0, 0, 0);

  if (vw_check16(cbuf)) return -1;

  koho_len = vw_after_henkan(cx_num, in_buf, wcs, 0);

  if (vw_check16(cbuf)) return -1;

  datalen = koho_len + 2;

  buffer_check(cbuf, 4 + datalen);
  header = (cannaheader_t *)cbuf->buf;
  sp = (ushort *)cbuf->buf;

  header->type = 0x14;
  header->extra = 0;
  header->datalen = LSBMSB16(datalen);
  sp[2] = LSBMSB16(cx->max_bun);
  memcpy(&sp[3], wcs, koho_len);
  
  return 1;  
}

int vjewrapper_store_range(int id, buffer_t *cbuf)
{
  VW_ERROR8(cbuf->buf);
  
  return 0;
}

int vjewrapper_get_lastyomi(int id, buffer_t *cbuf)
{
  cannaheader_t *header;
  ushort *sp = (ushort *)cbuf->buf;
  short cx_num, maxbun, datalen;
  int len, cvt, i;
  uchar *yomi, euc[IN_BUF_SIZE];
  ushort wcs[IN_BUF_SIZE];
  context_t *cx;
  
  cx_num = LSBMSB16(sp[2]);
  cx = vw_get_context(cx_num);

  maxbun = cx->max_bun - 1;

  for (i = cvt = 0; i < cx->max_bun; i++)
    cvt += cx->bun[i].yomi_len;

  i = cx->bun[maxbun].yomi_pnt + cx->bun[maxbun].yomi_len;

  yomi = &cx->yomi_buffer[i];

  len = sjis2euc(yomi, strlen(yomi), euc, IN_BUF_SIZE);

  m_msg_dbg("lastyomi = [%s]\n", euc);
  
  len = euc2cannawc(euc, len, wcs, IN_BUF_SIZE);

  datalen = 2 + len * 2 + 2;

  buffer_check(cbuf, datalen + 4);
  header = (cannaheader_t *)cbuf->buf;
  sp = (ushort *)cbuf->buf;

  header->type = 0x16;
  header->extra = 0;
  header->datalen = LSBMSB16(datalen);
  sp[2] = LSBMSB16(len);
  memcpy(&sp[3], wcs, len * 2 + 2);

  return 1;
}

int vjewrapper_flush_yomi(int id, buffer_t *cbuf)
{
  cannaheader_t *header = (cannaheader_t *)cbuf->buf;
  ushort *sp = (ushort *)cbuf->buf;
  short cx_num, mode = 1, datalen;
  int i, cvt, koho_len;
  context_t *cx;

  uchar in_buf[IN_BUF_SIZE], keybuf[IN_BUF_SIZE];
  ushort atrbuf[IN_BUF_SIZE * 2], wcs[IN_BUF_SIZE * 2];

  cx_num = LSBMSB16(sp[2]);
  cx = vw_get_context(cx_num);

  if (cx->subst_yomi_zero_flag)
    mode = 3;
  
  memcpy(in_buf, cx->yomi_buffer, IN_BUF_SIZE);

  vw_make_vjebuffers(in_buf, atrbuf, keybuf);

  /* どうやら bno には変換されている文節数を渡すらしい(自動変換のときのみ) */
  vje_proto_set_koho_bno(cx->vje.id, cx->max_bun);

  /* 変換されている読みの長さを取得 */
  for (i = cvt = 0; i < cx->max_bun; i++)
    cvt += cx->bun[i].yomi_len;

  vje_proto_chg_sdic(cx->vje.id, -1);
  vje_proto25_set_kihonbuff(cx->vje.id, in_buf, atrbuf, keybuf);
  vje_proto25_henkanb(cx->vje.id, in_buf, atrbuf, keybuf, &mode, cvt);

  if (vw_check16(cbuf)) return -1;

  koho_len = vw_after_henkan(cx_num, in_buf, wcs, 0);

  if (vw_check16(cbuf)) return -1;

  cannawc2euc(wcs, cannawcstrlen(wcs), in_buf, IN_BUF_SIZE);
  
  datalen = koho_len + 2;

  buffer_check(cbuf, 4 + datalen);
  header = (cannaheader_t *)cbuf->buf;
  sp = (ushort *)cbuf->buf;

  header->type = 0x17;
  header->extra = 0;
  header->datalen = LSBMSB16(datalen);
  sp[2] = LSBMSB16(cx->max_bun);
  memcpy(&sp[3], wcs, koho_len);
  
  return 1;
}

int vjewrapper_remove_yomi(int id, buffer_t *cbuf)
{
  cannaheader_t *header = (cannaheader_t *)cbuf->buf;
  short *sp = (short *)cbuf->buf;
  int *ip = (int *)cbuf->buf;
  int i, koho_no, bun_no, len, pnt;
  short cx_num, datalen;
  char err;
  context_t *cx;
  
  char tmpbuf[IN_BUF_SIZE], in_buf[HIRA_BUF_SIZE], euc[IN_BUF_SIZE];
  ushort wcs[IN_BUF_SIZE * 2];

  datalen = LSBMSB16(sp[1]);
  cx_num = LSBMSB16(sp[2]);
  bun_no = LSBMSB16(sp[3]);

  cx = vw_get_context(cx_num);

  err = (cx->vje.id) ? 0 : -1;

  if (err == 0) {
    vje_proto_set_koho_bno(cx->vje.id, cx->max_bun - 1);
    
    if (ip[2] != 0) {
      for (i = 0; i <= bun_no; i++) {
	if (datalen >= 8 + (i + 1) * 2) {
	  vw_move_bunsetu(cx, i);
	  
	  koho_no = LSBMSB16(sp[6 + i]);
	  
	  vw_set_koho(cx_num, i, koho_no);
	}
      }

      if (vw_check8(cbuf)) return -1;
    }

    vw_move_bunsetu(cx, bun_no);
    
    memcpy(in_buf, cx->yomi_buffer, HIRA_BUF_SIZE);
    
    i = vje_proto25_kakutei2(cx->vje.id, in_buf, tmpbuf);
    
    if (vw_check8(cbuf)) return -1;
    
    pnt = cx->bun[bun_no].yomi_pnt + cx->bun[bun_no].yomi_len;
    len = LEN_OF_OLDBUF(in_buf) - pnt + 1 + 1;
    
    memmove(&in_buf[1], &in_buf[pnt], len);
    LEN_OF_OLDBUF(in_buf) = strlen(&in_buf[1]);

    sjis2euc(in_buf + 1, in_buf[0], euc, IN_BUF_SIZE);
    
    vw_after_henkan(cx_num, in_buf, wcs, 0);
  }

  header->type = 0x18;
  header->extra = 0;
  header->datalen = LSBMSB16(1);
  header->err.e8 = cx->max_bun;
  
  return 1;
}

int vjewrapper_get_simplekanji(int id, buffer_t *cbuf)
{
  VW_ERROR16(cbuf->buf);
  
  return 0;
}

int vjewrapper_resize_pause(int id, buffer_t *cbuf)
{
  int i, j, vjeyomilen = 0, bun_no, cx_num, koho_len, datalen;
  short cannayomilen;
  int nbun, len, len2;
  short *sp = (short *)cbuf->buf;
  cannaheader_t *header;
  context_t *cx;
  uchar in_buf[IN_BUF_SIZE];
  ushort wcs[IN_BUF_SIZE * 2];

  buninf25_t bi;
  
  cx_num = LSBMSB16(sp[2]);
  bun_no = LSBMSB16(sp[3]);
  cannayomilen = LSBMSB16(sp[4]);

  cx = vw_get_context(cx_num);

  if (cx->vje.id == 0) {
    VW_ERROR16(cbuf->buf);
    return 1;
  }

  vje_proto25_get_koho_all(cx->vje.id, &bi, in_buf);
  
  if (cannayomilen == -1) { /* 文節伸ばし */
    if (bun_no + 1 >= cx->max_bun)
      vjeyomilen = cx->bun[bun_no].yomi_len;
    else {
      vjeyomilen = cx->bun[bun_no].yomi_len;
      if (ISSJISKANJI1(cx->yomi_buffer[cx->bun[bun_no + 1].yomi_pnt]))
	vjeyomilen += 2;
      else
	vjeyomilen++;
    }
  } else if (cannayomilen == -2) { /* 文節縮め */
    for (i = j = 0; i < cx->bun[bun_no].yomi_len;) {
      if (ISSJISKANJI1(cx->yomi_buffer[cx->bun[bun_no].yomi_pnt + i]))
	j = 2;
      else
	j = 1;
      i += j;
    }

    vjeyomilen = cx->bun[bun_no].yomi_len - j;
    if (vjeyomilen <= 0)
      vjeyomilen = cx->bun[bun_no].yomi_len;
  } else if (cannayomilen > 0) {
    j = vjeyomilen = 0;
    while (j != cannayomilen) {
      if (ISSJISKANJI1(cx->yomi_buffer[cx->bun[bun_no].yomi_pnt + vjeyomilen]))
	vjeyomilen += 2;
      else
	vjeyomilen++;
      j++;
    }
  }

  if (vjeyomilen > 0) {
    i = bun_no; len2 = 0;
    for (;;) {
      len2 += cx->bun[i].yomi_len;

      if (vjeyomilen <= len2)
	break;

      i++;
    }

    nbun = 1 + i - bun_no;
    len = vjeyomilen;
    memcpy(in_buf, cx->yomi_buffer, IN_BUF_SIZE);

    vw_move_bunsetu(cx, bun_no);

    vje_proto25_saihenkan(cx->vje.id, in_buf, nbun, len2, len, 5);

    if (vw_check16(cbuf)) return -1;

    koho_len = vw_after_henkan(cx_num, in_buf, wcs, bun_no);

    if (vw_check16(cbuf)) return -1;
    
    datalen = koho_len + 2;

    buffer_check(cbuf, datalen + 4);
    header = (cannaheader_t *)cbuf->buf;
    sp = (short *)cbuf->buf;

    header->type = 0x1a;
    header->extra = 0;
    header->datalen = LSBMSB16(datalen);
    sp[2] = LSBMSB16(cx->max_bun);
    memcpy(&(sp[3]), wcs, koho_len);
  } else {
    VW_ERROR16(cbuf->buf);
  }

  return 1;
}

int vjewrapper_get_hinshi(int id, buffer_t *cbuf)
{
  cannaheader_t *header = (cannaheader_t *)cbuf->buf;

  header->type = 0x1b;
  header->extra = 0;
  header->datalen = LSBMSB16(1);
  header->err.e8 = 0;
  
  return 1;
}

int vjewrapper_get_lex(int id, buffer_t *cbuf)
{
  VW_ERROR16(cbuf->buf);
  
  return 0;
}

int vjewrapper_get_status(int id, buffer_t *cbuf)
{
  struct {
    int bunnum;
    int candnum;
    int maxcand;
    int diccand;
    int ylen;
    int klen;
    int tlen;
  } stat;

  short bun_no, koho_no, cx_num;
  short *sp = (short *)cbuf->buf;
  int i, koho_num, koholen;
  cannaheader_t *header;
  context_t *cx;
  uchar euc[IN_BUF_SIZE];
  ushort wcs[IN_BUF_SIZE];
  char *koho_inf;

  cx_num = LSBMSB16(sp[2]);
  bun_no = LSBMSB16(sp[3]);
  koho_no = LSBMSB16(sp[4]);

  cx = vw_get_context(cx_num);

  if (cx->vje.id == 0) {
    VW_ERROR8(cbuf->buf);
    return 1;
  }

  i = vw_get_yomi(cx, bun_no, euc);
  i = euc2cannawc(euc, i, wcs, IN_BUF_SIZE);
  stat.ylen = LSBMSB32(i); /* カレント候補の読みがなのバイト数 */

  if (cx->bun[bun_no].koho == NULL) {
    /* まだ候補を取得してないなら取得する */
    cx->bun[bun_no].koho = vw_get_koho_list(cx, bun_no, &koho_num, &koholen,
					    &koho_inf);
    cx->bun[bun_no].koho_inf = koho_inf;
    cx->bun[bun_no].koho_num = koho_num;
    cx->bun[bun_no].koho_len = koholen;
  }
  
  i = cannawcstrlen(vw_retrieve_koho(cx_num, bun_no, koho_no));

  stat.klen = LSBMSB32(i); /* カレント候補の漢字候補のバイト数 */
  
  stat.tlen = LSBMSB32(1); /* カレント候補の構成単語数 */

  i = cx->bun[bun_no].koho_num;
  
  stat.maxcand = LSBMSB32(i); /* カレント文節の候補数 */

  i -= m_count_canna_mode(cx->canna_mode);
  
  stat.diccand = LSBMSB32(i); /* maxcand - モード指定分 */
  
  stat.bunnum = LSBMSB32(bun_no); /* カレント文節番号 */
  stat.candnum = LSBMSB32(koho_no); /* カレント候補番号 */
				      
  if (vw_check8(cbuf)) return -1;

  buffer_check(cbuf, 33);
  header = (cannaheader_t *)cbuf->buf;

  header->type = 0x1d;
  header->extra = 0;
  header->datalen = LSBMSB16(29);

  cbuf->buf[4] = 0;

  memcpy(&(cbuf->buf[5]), (char *)&stat, 28);

  return 1;
}

int vjewrapper_set_locale(int id, buffer_t *cbuf)
{
  VW_ERROR8(cbuf->buf);
  
  return 0;
}

int vjewrapper_auto_convert(int id, buffer_t *cbuf)
{
  cannaheader_t *header = (cannaheader_t *)cbuf->buf;
  short *sp = (short *)cbuf->buf;
  int *ip = (int *)cbuf->buf;
  int cmode;
  short cx_num;
  context_t *cx;

  cx_num = LSBMSB16(sp[2]);
  cmode = LSBMSB32(ip[2]);

  cx = vw_get_context(cx_num);

  if (vw_check8(cbuf)) return -1;

  if (cx->vje.id == 0)
    vw_open_vje_library(cx_num); /* 失敗したら cx->vje.id は 0 のまま */

  if (vw_check16(cbuf)) return -1;

  if (cx->vje.id) {
    vje_proto_clear(cx->vje.id);
  
    vw_clear_context(cx_num);

    cx->subst_yomi_zero_flag = 0;
    cx->canna_mode = cmode;

    /* 必要ならここでコンテキストにフラグをたてる */

    header->datalen = LSBMSB16(1);
    header->err.e8 = 0;
  } else {
    header->datalen = LSBMSB16(1);
    header->err.e8 = -1;
  }
  
  return 1;
}


