/*  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 <sys/stat.h>
#include <fcntl.h>

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

typedef struct _vje30_t {
  char *dicfile[10]; /* 辞書へのパス */
  short dicmode[10]; /* 辞書を開く時の mode */
  char *romaji; /* VJE30: ローマ字ファイルのパス */
  char *ktable; /* VJE30: 漢字テーブルのパス */
  char *restart; /* vjed を再起動するコマンドへのフルパス */
  char katakana_gakushu:1; /* カタカナに変換したのを学習する? */
  char restart_vjed:1; /* vjed が落ちたとき、再起動する? 終了する? */
} vje30_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; /* かんなの変換モード */
  
  buff_t yomi_buffer; /* 読みのバッファ. 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 char *vjelibpath;

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, sizeof(buff_t));

  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, mode;
  char *dicfname, *dicpath, buf[DIC_NAME_SIZE + 10];
  
  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++) {
      dicfname = client[id].data.vje30->dicfile[i];
      mode = client[id].data.vje30->dicmode[i];
      dicpath = NULL;

      if (dicfname) {
	if (i == 9 && client[id].homedir != NULL) {
	/* ユーザー辞書の場合で、なおかつ id != VJE_ROOT_CLIENT の場合 */
	  sprintf(buf, ".vje/%s", dicfname);
	  dicpath = m_makepath(client[id].homedir, buf);
	} else {
	  sprintf(buf, "dic/%s", dicfname);
	  dicpath = m_makepath(vjelibpath, buf);
	}
	
	m_msg("VJE30.Dic: #%d, [%s] %d\n", i, dicpath, mode);
	
	vje_proto_clear(vje_id);
	vje_proto_open_sdic(vje_id, i, dicpath, mode);
	
	cx->vje.sdic_opened_flag[i] = 1;
	free(dicpath);
      }
    }

    /* OpenKanjiTable, VRoomajiOpen */

    vje_proto_vroomaji_open(vje_id, client[id].data.vje30->romaji, "ROMA");
    vje_proto_open_kanji_table(vje_id, client[id].data.vje30->ktable);
    
    mode = 0x5700;
    mode |= client[id].data.vje30->katakana_gakushu ? 0x40 : 0;

    vje_proto_setmode(vje_id, mode, 0x300, 10, 3);
    
    vje_proto_setmode(vje_id, mode, 0x10b, 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];
  char *ope, *val;
  int i, ret = 0;
  vje30_t *vje30 = client[id].data.vje30;

  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) {
      i = M_CONF_ONOFF(ope, "VJE30.KatakanaGaku", val);
      if (i) vje30->katakana_gakushu = i - 1;
      
      if (id == VJE_ROOT_CLIENT) {
	m_conf_string(ope, "VJE30.RestartComm", val, &(vje30->restart));
	
	i = M_CONF_ONOFF(ope, "VJE30.RestartVjed", val);
	if (i) vje30->restart_vjed = i - 1;
      }
    }
  }

  fclose(fp);

  return ret;
}

/* general.sty を読む */

static int vw_read_general_style(int id, char *styfilepath)
{
  FILE *fp;
  char *ope, *val, buf[1024], dicbuf[10];
  int roma, ktbl, dic, i, dicmode;
  vje30_t *vje30 = client[id].data.vje30;

  roma = ktbl = dic = dicmode = 0;

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

  while (fgets(buf, 1024, fp)) {
    if (buf[0] == '[') {
      roma = ktbl = dic = dicmode = 0;
      
      if (strstr(buf, "[Roma]"))
	roma = 1;
      if (strstr(buf, "[DicInfo]"))
	ktbl = 1;

      for (i = 1; i <= 10; i++) {
	sprintf(dicbuf, "[DIC%d]", i);
	
	if (strstr(buf, dicbuf))
	  dic = i;
      }
    } else if ((roma || ktbl || dic) && m_conf1_parse(buf, &ope, &val) == 0) {
      if (roma)
	m_conf_string(ope, "RomFile", val, &(vje30->romaji));
      if (ktbl)
	m_conf_string(ope, "DicDat", val, &(vje30->ktable));
      if (dic) {
	m_conf_string(ope, "DicFile", val, &(vje30->dicfile[dic - 1]));
	
	if (m_conf_isequal(ope, "LearnMode", val, "YES") == 2)
	  dicmode |= 0x06;
	if (m_conf_isequal(ope, "LearnMode", val, "DON") == 2)
	  dicmode |= 0x02;
	if (m_conf_isequal(ope, "LearnType", val, "MYSELF") == 2)
	  dicmode |= 0x10;
	if (m_conf_isequal(ope, "Parallel", val, "YES") == 2)
	  dicmode |= 0x20;
	if (dic == 10)
	  dicmode |= 0x01;

	vje30->dicmode[dic - 1] = dicmode;
      }
    }
  }

  fclose(fp);

  if (vje30->romaji == NULL || vje30->ktable == NULL)
    return -1;

  return 0;
}

/* vje.cfg を読む */

static int vw_read_vje_cfg()
{
  FILE *fp;
  char *ope, *val, buf[1024];

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

  while (fgets(buf, 1024, fp)) {
    if (buf[0] != '[' && m_conf1_parse(buf, &ope, &val) == 0)
      m_conf_string(ope, "Path", val, &vjelibpath);
  }

  fclose(fp);

  if (vjelibpath == NULL)
    return -1;

  /* パスの最後が / で終わってるなら やめさせる */
  if (vjelibpath[strlen(vjelibpath) - 1] == '/')
    vjelibpath[strlen(vjelibpath) - 1] = 0;

  m_msg_dbg("VJE Library Path = %s\n", vjelibpath);

  return 0;
}  

static int vw_config_client(int id, char *conffile)
{
  char *stypath = NULL;
  FILE *src1, *src2, *dst1, *dst2;
  int len;
  vje30_t *vje30;

  if ((vje30 = client[id].data.vje30 = calloc(1, sizeof(vje30_t))) == NULL) {
    m_msg("Out of Memory.\n");
    return -1;
  }
  
  if (client[id].homedir && (len = strlen(client[id].homedir)) > 0) {
    if (client[id].homedir[len - 1] == '/')
      client[id].homedir[len - 1] = 0;
  } else {
    client[id].homedir = NULL;
  }
  
  /* /etc/esecannarc か ~/.esecannarc を読む */
  if (vw_read_conf_file(id, conffile) == -1)
    return -1;

  if (id != VJE_ROOT_CLIENT && client[id].homedir) {
    if (chdir(client[id].homedir) == 0) {
      if (chdir(".vje") != 0) {
	/* ~/.vje がない場合はここで作成し、ファイルをコピー */
	dst1 = dst2 = src1 = src2 = NULL;
	
	if (mkdir(".vje", 755) == 0 && chdir(".vje") == 0) {
	  dst1 = fopen("general.sty", "w");
	  dst2 = fopen("vjed95u.dic", "w");

	  if (dst1 && dst2) {
	    chdir(vjelibpath);
	    chdir("com");

	    src1 = fopen("general.sty", "r");
	    src2 = fopen("vjed95u.dic", "r");

	    if (src1 && src2) {
	      m_copy_file_fp(src1, dst1);
	      m_copy_file_fp(src2, dst2);
	    }
	  }

	  if (dst1) fclose(dst1);
	  if (dst2) fclose(dst2);
	  if (src1) fclose(src1);
	  if (src2) fclose(src2);
	}
      }

      stypath = "general.sty";
    }
  } else {
    chdir(vjelibpath);
    stypath = "com/general.sty";
  }

  if (stypath == NULL)
    return -1;
  
  if (vw_read_general_style(id, stypath) == -1)
    return -1;
  
  return 0;
}

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

static int vw_check8(buffer_t *cbuf)
{
  extern int vjeerror;
  
  if (vjeerror) {
    /* vje25 のソースを読め。
    */

    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(buff_t *in_buf, ushort *atrbuf, kbuff_t *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_BUFP(in_buf)) {
    zenkana_flag = 0;
    if (in_pnt + 1 <= LEN_OF_BUFP(in_buf) &&
	(ISSJIS_ZENKAKU_HIRAGANA(in_buf->str[in_pnt], in_buf->str[in_pnt + 1])
	 || ISSJIS_ZENKAKU_VU(in_buf->str[in_pnt], in_buf->str[in_pnt + 1]))) {
      /* ひらがな,または「ヴ」の場合 */
      if (in_pnt + 3 <= LEN_OF_BUFP(in_buf) &&
	  (i = vw_lookup_romaji_table(&(in_buf->str[in_pnt]), 4)) != -1) {
	/* "にゃ" のような読みの場合 */
	strcpy(roma, romatbl[i].romaji);
	d_in_pnt = 4;
      } else if ((i=vw_lookup_romaji_table(&(in_buf->str[in_pnt]), 2)) != -1) {
	/* "に" のような読みの場合 */
	strcpy(roma, romatbl[i].romaji);
	d_in_pnt = 2;
      }
    } else if (in_pnt + 1 <= LEN_OF_BUFP(in_buf) &&
	       ISSJIS_ZENKAKU_KATAKANA(in_buf->str[in_pnt],
				       in_buf->str[in_pnt + 1])) {
      zenkana_flag = 1;
      /* カタカナの場合 */
      d_in_pnt = -1;
      if (in_pnt + 3 <= LEN_OF_BUFP(in_buf) &&
	  ISSJIS_ZENKAKU_KATAKANA(in_buf->str[in_pnt + 2],
				  in_buf->str[in_pnt + 3])) {
	/* 次の読みもカタカナの場合 */
	m_convert_zenkana2zenhira(&(in_buf->str[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->str[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->str[in_pnt])) {
      /* "a" "@" "「" などの全角非ひらがな */
      if ((roma[0] = m_convert_zen2han(&(in_buf->str[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->str[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->str[key_pnt]), roma, romaslen);
    key_pnt += romaslen;
    in_pnt += d_in_pnt;
  }

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

  return 0;
}

static int vw_move_bunsetu(context_t *cx, int n)
{

  buninf_t bi;
  doubufx_t douon;
  buff_t tmpbuf;
  uchar euc[IN_BUF_SIZE];
#if 0
  while (cx->cur_bun_no < n) {
    vje_proto_ji_bunsetu(cx->vje.id);
    cx->cur_bun_no++;
    vje_proto_get_koho_all(cx->vje.id, &bi, &tmpbuf);
    vje_proto_get_douon(cx->vje.id, &douon);
    vje_proto_set_koho(cx->vje.id, 1);
  }

  while (cx->cur_bun_no > n) {
    vje_proto_zen_bunsetu(cx->vje.id);
    cx->cur_bun_no--;
  }
  
#endif
  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.str[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, end;
  uchar sjis[IN_BUF_SIZE], euc[IN_BUF_SIZE], tmpbuf[IN_BUF_SIZE];
  ushort wcs[IN_BUF_SIZE];
  buff_t in_buf;
  kbuff_t keybuf;
  ushort atrbuf[IN_BUF_SIZE * 2];
  short size, mode = 1;
  char *koho_inf;
  uchar yomi_len;
  int hira_koho, kata_koho;
  
  extern int vjeerror;
  
  buffer_t zbuf, zbuf2;
  doubufx_t douon;
  buninf_t bi;

  zbuf.size = zbuf2.size = 0;
  zbuf.buf = zbuf2.buf = NULL;
  hira_koho = kata_koho = -1;

  vw_move_bunsetu(cx, bun_no);
  
  vje_proto_get_koho_all(cx->vje.id, &bi, &in_buf);
  yomi_len = bi.kouho[bun_no].yomi_len;

  koho_num = size = buf_pnt = 0;
  
  vje_proto_get_douon(cx->vje.id, &douon);

  /* 候補数が 1 なら無視 */
  if (douon.cnt == 1) {
    vje_proto_ji_block(cx->vje.id);
    vje_proto_get_douon(cx->vje.id, &douon);

    /* 「だろう」を変換すると候補は「だろう」だけだから問題あり */
    vje_proto_get_koho_all(cx->vje.id, &bi, &in_buf);
    if (yomi_len != bi.kouho[bun_no].yomi_len) {
      vje_proto_zen_block(cx->vje.id);
      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;
  }

  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_koho = i;
      else if (m_is_hiragana_string(euc))
	hira_koho = i;
      
      m_msg_dbg("kouho[%d]=[%s]\n", koho_num, euc);
      
      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 = (uchar *)zbuf2.buf;
      koho_inf[koho_num - 1] = i;
    }
  }

  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 = (uchar *)zbuf2.buf;
      koho_inf[koho_num - 1] = kata_koho;
    } 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_koho;
    }
  }

  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;
  buff_t sjis;
  uchar euc[IN_BUF_SIZE];
  static ushort wcs[IN_BUF_SIZE];
  int i, j, slen, koho_num, koholen;
  char *koho_inf;
  buninf_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_proto_get_koho_all(cx->vje.id, &bi, &sjis);
    slen = sjis2euc(&(sjis.str[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 koho, i, len, pnt, n;
  context_t *cx;
  uchar hira[IN_BUF_SIZE], kata[IN_BUF_SIZE], sjis[IN_BUF_SIZE];
  uchar tmpbuf[IN_BUF_SIZE];
  ushort atrbuf[IN_BUF_SIZE * 2], *sp;
  buff_t in_buf;
  kbuff_t keybuf;
  doubufx_t douon;
  buninf_t bi;

  cx = vw_get_context(cx_num);

  if (koho_no > 0 && cx->bun[bun_no].koho == NULL) {
    int koho_num, koholen;
    char *koho_inf;
    
    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;
  }
  
  if (koho_no > 0) {
    koho = cx->bun[bun_no].koho_inf[koho_no];
    
    if (koho != -1) {
      vje_proto_get_douon(cx->vje.id, &douon);
      
      vje_proto_set_koho(cx->vje.id, 1 + koho);
      
      vje_proto_get_koho_all(cx->vje.id, &bi, &in_buf);

      sjis2euc(&in_buf.str[1], in_buf.len, tmpbuf, IN_BUF_SIZE);

      m_msg_dbg("get_koho = [%s]\n", tmpbuf);

      return 0;
    } else {
      /* koho == -1 の時は、カタカナ候補 */
      len = vw_get_yomi(cx, bun_no, hira);
      
      m_convert_zenhira2zenkata(hira, len, kata);

      m_msg_dbg("KATAKANA gakushu[%s]\n", kata);
      
      len = euc2sjis(kata, len, sjis, IN_BUF_SIZE);
      
      memcpy(&in_buf, &cx->yomi_buffer, sizeof(buff_t));
      memcpy(&(in_buf.str[cx->bun[bun_no].yomi_pnt]), sjis, len);
      
      vw_make_vjebuffers(&in_buf, atrbuf, &keybuf);
      
      vje_proto_set_kihonbuff(cx->vje.id, &in_buf, atrbuf, &keybuf);
      vje_proto_saihenkan(cx->vje.id, &in_buf, 1, len, len, 8);

      memcpy(&cx->yomi_buffer, &in_buf, sizeof(buff_t));
    }
  } else {
    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, buff_t *in_buf, ushort *wcs_r,
			   int bun_no)
{
  int i, pnt, len;
  uchar bufeuc[IN_BUF_SIZE];
  buff_t bufsjis;
  buninf_t bi;
  context_t *cx;
  doubufx_t douon;

  cx = vw_get_context(cx_num);

  vje_proto_get_koho_all(cx->vje.id, &bi, &bufsjis);
  vje_proto_get_douon(cx->vje.id, &douon);
  
  m_msg_dbg("kouho = ");
  
  pnt = 0;
  for (i = bun_no; i < bi.max_bun; i++) {
    len = sjis2euc(&(bufsjis.str[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, sizeof(buff_t));

  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 VJE30.\n");

  if (vw_read_vje_cfg() == -1) {
    m_msg("failed to determine vje library path.\n");
    return -1;
  }

  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;
  vje30_t *vje30 = client[id].data.vje30;

  for (i = 0; i < 10; i++)
    MYFREE(vje30->dicfile[i]);

  MYFREE(vje30->romaji);
  MYFREE(vje30->ktable);
  MYFREE(vje30->restart);

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

  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);

  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, i;
  short cx_num;
  context_t *cx;
  
  short vje_id, mode = 1;

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

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

  for (i = 0; i < m_count_canna_mode(cmode); i++) {
    m_msg_dbg("Mode #%d = %d\n", i, m_get_canna_mode(cmode, i));
  }
  
  cx = vw_get_context(cx_num);

  if (cx->vje.id == 0)
    vw_open_vje_library(cx_num); /* 失敗したら cx->vje.id は 0 のまま */
  
  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.str + 1, IN_BUF_SIZE - 1);
  LEN_OF_BUF(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(fd, vje_id, 0);
    */
    vje_proto_set_kihonbuff(vje_id, &in_buf, atrbuf, &keybuf);
    vje_proto_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 eucbuf[IN_BUF_SIZE];
  buff_t tmpbuf;

  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]);

      m_msg_dbg("KOUHO(%d) = %d\n", i, koho_no);

      vw_set_koho(cx_num, i, koho_no);
    }
    
    if (vw_check8(cbuf)) return -1;
    
    i = vje_proto_kakutei1(cx->vje.id, &cx->yomi_buffer, &tmpbuf);
    i = sjis2euc(tmpbuf.str + 1, tmpbuf.len, eucbuf, IN_BUF_SIZE);
    m_msg_dbg("kakutei = [%s]/%d\n", eucbuf, i);

    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; /* FIXME: IS THIS RIGHT? */
  
  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, len, errflag = 0, koho_len, datalen, cvt, i;
  ushort *yomi;
  uchar euc[IN_BUF_SIZE], sjis[IN_BUF_SIZE];
  buff_t in_buf;
  kbuff_t keybuf;
  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);

  memcpy(&in_buf, &cx->yomi_buffer, sizeof(buff_t));

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

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

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

	vw_make_vjebuffers(&in_buf, atrbuf, &keybuf);
	vje_proto_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);
      len = euc2sjis(euc, strlen(euc), sjis, IN_BUF_SIZE) + 1;

      if (newyomipnt + cvt + 1 + len > IN_BUF_SIZE)
	size = IN_BUF_SIZE - 1 - (newyomipnt + cvt + 1);
      else
	size = len;
      
      strncpy(&(in_buf.str[newyomipnt + cvt + 1]), sjis, size);

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

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

    vw_make_vjebuffers(&in_buf, atrbuf, &keybuf);

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

    if (cx->subst_yomi_zero_flag == 0) {
      /* ただ単にバックスペースされた場合は, henkanb を呼ばないっぽい。*/
      vje_proto_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];
  ushort atrbuf[IN_BUF_SIZE * 2], wcs[IN_BUF_SIZE * 2];
  buff_t in_buf;
  kbuff_t keybuf;

  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

  /* FIXME IF NECESSARY: OBSOLETE CODE!!! ONLY WORKS ON VJE 2.5 */
  /* 厳密にはこのコードが必要かも知れないが、なくてもまともに動く? */

  memset(&in_buf, 0, sizeof(buff_t));

  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_BUF(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, sizeof(buff_t));

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

  in_buf.str[pnt] = 0;
  LEN_OF_BUF(in_buf) = strlen(&in_buf.str[1]);

  vw_make_vjebuffers(&in_buf, atrbuf, &keybuf);

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

  if (len > 0)
    vje_proto_saihenkan(cx->vje.id, &in_buf, 1, len, len, 5);
  else
    vje_proto_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.str[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;

  buff_t in_buf;
  kbuff_t keybuf;
  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, sizeof(buff_t));

  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_proto_set_kihonbuff(cx->vje.id, &in_buf, atrbuf, &keybuf);
  vje_proto_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 = 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 euc[IN_BUF_SIZE];
  ushort wcs[IN_BUF_SIZE * 2];
  buff_t in_buf;
  buff_t tmpbuf;
  

  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, sizeof(buff_t));
    
    i = vje_proto_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_BUF(in_buf) - pnt + 1 + 1;
    
    memmove(&in_buf.str[1], &in_buf.str[pnt], len);
    LEN_OF_BUF(in_buf) = strlen(&in_buf.str[1]);

    sjis2euc(in_buf.str + 1, LEN_OF_BUF(in_buf), 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;
  buff_t in_buf;
  ushort wcs[IN_BUF_SIZE * 2];

  buninf_t bi;
  doubufx_t douon;
  
  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_proto_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.str[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.str[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.str[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, sizeof(buff_t));

    vw_move_bunsetu(cx, bun_no);

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

    if (vw_check16(cbuf)) return -1;

    /* 注目分節の候補数が1の場合、vje の仕様で次のブロックに
       ひらがな、カタカナ,半角候補が現れる。さらに ji_block したときに
       別の分節の候補が変わることがある。その対策。
       */

    vje_proto_get_douon(cx->vje.id, &douon);
    if (douon.cnt == 1)
      vje_proto_ji_block(cx->vje.id);
    vje_proto_saihenkan(cx->vje.id, &in_buf, 1, len, len, 5);

    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 (cx->vje.id == 0)
    vw_open_vje_library(cx_num); /* 失敗したら cx->vje.id は 0 のまま */

  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;
}


syntax highlighted by Code2HTML, v. 0.9.1