/* # skkinput (Simple Kana-Kanji Input)
 * skkel.c --- Emacs-lisp -> C
 * This file is part of skkinput.
 * Copyright (C) 1997
 * Takashi SAKAMOTO (sakamoto@yajima.kuis.kyoto-u.ac.jp)
 *
 * 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
 * the Free Software Foundation; either version 2, 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 skkinput; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
 * このソースはどのウィジェットにも属さない(？)関数の集合であるとみな
 * す。従って、どのウィジェットからも呼び出すことが可能である…とする。
 * そうでないと非常に面倒だから。(^^;;;;
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#include <sys/types.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/ShellP.h>

#include "commondef.h"
#include "buffers.h"
#include "skkbuf.h"
#include "SeparateWin.h"
#include "OverWin.h"
#include "table.h"
#include "skkel.h"
#include "skkkey.h"

/*
 * プロトタイプ宣言。
 */
/* skkel.c */
static int delete_char
( struct SKKInputNode *node, int pos, int count ) ;
static int j_insert_myChara
( struct SKKInputNode *node, int pos, struct myChar wchara ) ;
static int j_insert_str
( struct SKKInputNode *node, int pos, struct myChar *str ) ;
static void j_n_convert_to_kana( struct SKKInputNode *node ) ;
static void j_input_by_code_or_menu_jmp_showCandidate
( struct SKKInputNode *node, int code ) ;
static int j_compute_numeric_henkan_key
( struct myChar *dptr, struct myChar *sptr, struct myChar *j_num_list ) ;
static int j_insert_word
( struct SKKInputNode *node, int pos, struct myChar *word ) ;
static int j_start_henkan_sub( Widget gw, struct SKKInputNode *node ) ;
static int j_numeric_convert
( struct SKKInputNode *node,
  struct myChar *sptr, struct myChar *dptr, int dbufsize ) ;
static struct myChar *j_num_exp
( struct myChar *j_num_list_ptr, int type, struct myChar *dptr,
  int dbufsize ) ;
static int j_quote_char( struct myChar *word ) ;
static int j_quote_semicolon( struct myChar *word ) ;
static int save_ringbuffer
( struct myChar *buffer, int bufsize, int *startpos, int *endpos,
  struct myChar *string ) ;
static void j_keyboard_quit_henkanrestart
( Widget gw, struct SKKInputNode *node, int flag ) ;
static int find_next_completion( struct SKKInputNode *node ) ;

/* skkmbuf.c */
extern int skkinput_j_henkan_in_minibuff_init
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node ) ;
extern void free_Minibuffer
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node ) ;
extern int j_read_string
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, char *string, int minibuff_usage ) ;

/* skkwin.c */
/* skksvect.c */
extern int copyCandidate
( struct myChar *dest, SkkinpSearchVector *sNode, int position, int length ) ;
/* skkmarker.c */
extern int move_marker_ByInsert
( struct SKKInputNode *node, int startpoint, int effect ) ;
extern int move_marker_ByDeleteRegion
( struct SKKInputNode *node, int startpoint, int endpoint ) ;
extern int set_marker_ByGeqPosition
( struct SKKInputNode *node, int startpos, int setpos ) ;

/* skkldrec.c*/
extern void add_SudeniHenkanKakuteiShitamono
( struct myChar *henkankey, VectorIndex **vectorindextop,
  VectorIndex *hresult, int okuri ) ;
extern void add_SudeniHenkanKakuteiShitamonoByString
( struct myChar *henkankey, struct myChar *hresult, int okuri ) ;
extern SkkinpSearchVector *searchSudeniHenkanKakuteiShitamono
( struct myChar *henkankey, SkkinpSearchVector *top, int okuri ) ;
extern SkkinpSearchVector *searchSudeniPurgeSaretamono
( struct myChar *henkankey, SkkinpSearchVector *top, int okuri ) ;
extern void purge_SudeniHenkanKakuteiShitamono
( struct SKKInputNode *node ) ;
extern SkkinpSearchVector *j_completion_search_sudeniKakuteiShitaKouho
( SkkinpSearchVector *top, struct myChar *completion_key ) ;
extern SkkinpSearchVector *j_completion_search_sudeniPurgeSaretamono
( SkkinpSearchVector *top, struct myChar *completion_key ) ;

/* skkldic.c */
extern SkkinpSearchVector *j_search_skkinput_local_jisyo
( struct myChar *key, SkkinpSearchVector *top, int okuri ) ;
extern SkkinpSearchVector *j_completion_skkinput_local_jisyo
( SkkinpSearchVector *top, struct myChar *completion_key ) ;

/* skksvect.c */
extern void free_VectorIndex( VectorIndex **top ) ;
extern void free_SkkinpSearchVector( SkkinpSearchVector **top ) ;
extern VectorIndex *makeSearchVectorIndex
( SkkinpSearchVector *top, int *totalnum, int flag ) ;

/* skksoc.c */
extern SkkinpSearchVector *skkinput_Request
( struct myChar *str, SkkinpSearchVector *top ) ;

/* lispeval.c */
extern int skkinputlisp_eval
( struct myChar *commandstring, struct myChar *buffer ) ;
extern int skkinput_create_j_num_list
( struct myChar *string ) ;

/*
 * グローバル変数。
 */
/* 削除、置換、挿入とかの際に用いるべきテンポラリのバッファ。*/
struct myChar temporary_buffer[ TEXTBUFSIZE * 2 ] ;
/* 漢字一文字のバイト数。*/
static const int j_input_by_code_or_menu_jump_default = 161 ;

static char *j_input_by_code_menu_keys1 = "asdfghqwerty" ;
static char *j_input_by_code_menu_keys2 = "asdfghjklqwertyu" ;
static unsigned char j_input_by_code_menu1_codes[] =
{ 33, 49, 65, 81, 97, 113 } ;
static const int j_code_n1_min = 33, j_code_n1_max = 116 ;
static const int j_code_n2_min = 33, j_code_n2_max = 126 ;

/*
 * 外部参照しているグローバル変数。
 */
extern struct skk_rom_kana_rule *skkinput_rom_kana_rule_list ;
extern int                       skkinput_use_numeric_conversion ;
extern struct myChar            *skkinput_input_vector[ 128 ] ;
extern struct myChar            *skkinput_zenkaku_vector[ 128 ] ;
extern int                       skkinput_date_ad ;
extern int                       skkinput_number_style ;
extern int                       skkinput_delete_implies_kakutei ;
extern int                       skkinput_use_numeric_conversion ;
extern int                       skkinput_tab_width ;
extern unsigned long             skkinput_j_count_kakutei ;
extern unsigned long             skkinput_j_count_touroku ;
extern Boolean                   skkinput_jisyo_dirty ;
extern int			 skkinput_dabbrev_like_completion ;
extern Boolean			 skkinput_autosave_jisyo_dirty ;

/*
 * エラーの表示関数。直にもうちょっとましなのに変化することであらう。
 */
void err_msg( struct SKKInputNode *node, char *fmt, ... )
{
  unsigned char tempbuffer[ TEXTBUFSIZE ] ;
  va_list valist ;
  va_start( valist, fmt ) ;
  vsprintf( tempbuffer, fmt, valist ) ;
  va_end( valist ) ;
  /* これでは漢字が落ちてしまう、どうにかすること！ */
  myCharCharStrncpy( node->mtextbuffer, tempbuffer, TEXTMAXLEN ) ;
  MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ TEXTMAXLEN ] ) ;
  return ;
}

/*
 * 文字を文字列に直して返す関数…って、何でこんなの必要なの？ とか言わ
 * れそう。
 */
char *charaToString( int chara )
{
  static char cbuf[ 4 ] ;
  const char dispChara[] = "[\\]^_" ;

  /* ここの部分は、京都大学情報工学科富田研究室の後藤氏のパッチです。*/
  /*----- ここから ----- ここから ----- ここから ----- ここから -----*/
  if( chara == 0x1b ) {/*ESCは"ESC"とする*/
    cbuf[ 0 ] = 'E';
    cbuf[ 1 ] = 'S';
    cbuf[ 2 ] = 'C';
    cbuf[ 3 ] = '\0';
  } else if( chara < 0x20 || chara == 0x7F ){
  /*----- ここまで ----- ここまで ----- ここまで ----- ここまで -----*/
    cbuf[ 0 ] = 'C' ;
    cbuf[ 1 ] = '-' ;
    cbuf[ 3 ] = '\0' ;
    if( chara == 0 ){
      /* Del key の場合。*/
      cbuf[ 2 ] = '@' ;
    } else if( chara == 0x7F ){
      /* Del key の場合。*/
      cbuf[ 2 ] = '?' ;
    } else if( chara >= 0x1b ){
      /* それ以外のコントロールコードの場合でアルファベットじゃない。*/
      cbuf[ 2 ] = dispChara[ chara - 0x1b ] ;
    } else {
      /* アルファベットで表現できるコントロールコードの場合。*/
      cbuf[ 2 ] = ( 'a' - 1 ) + chara ;
    }
  } else {
    cbuf[ 0 ] = chara ;
    cbuf[ 1 ] = '\0' ;
  }
  return cbuf ;
}

static void j_n_convert_to_kana( struct SKKInputNode *node )
{
  static struct myChar string_nn[] = {
    { CHARSET_JISX0208_1983, 0x2473 },
    { CHARSET_JISX0208_1983, 0x2573 },
  } ;
  delete_char( node, node->j_kana_start_point, 1 ) ;
  j_insert_myChara
    ( node, node->j_kana_start_point, 
      string_nn[ node->j_katakana_mode ] ) ;
  return ;
}

static struct myChar last_character_of( struct myChar *prefix )
{
  int length = myCharStrlen( prefix ) ;
  struct myChar chara ;
  if( length > 0 ){
    return prefix[ length - 1 ] ;
  } 
  MYCHAR_SET_END_OF_STRING( chara ) ;
  return ( chara ) ;
}

/*
 * 送り仮名モードに移行してよいかどうか判断する関数。
 */
static int is_okurigana_start_ok( struct SKKInputNode *node )
{
  struct myChar pchara ;
  int pos ;
  /* カーソル位置にいきなり大文字が入ってくれたのなら良いけど、最初 *
   * 小文字で入れて次に大文字で送り仮名なんて言われた場合には、仮名 *
   * 入力開始位置から見ないと駄目だから。*/
  pos = ( node->j_kana_mode )? node->j_kana_start_point : node->cur_pos ;
  /* カーソル位置と変換開始位置が等しければ駄目である。*/
  if( pos == node->j_henkan_start_point || pos <= 0 )
    return False ;
  /* 前に入力された文字が何であるかが問題になる。*/
  pchara = node->textbuffer[ pos - 1 ] ;
  if( pchara.charset == CHARSET_JISX0208_1983 ){
    /* 一つ前の文字が "０１２３４５６７８９" の中のどれかなら送り仮 *
     * 名モードにはならない。*/
    if( pchara.chara >= 0xA3B0 && pchara.chara <= 0xA3B9 )
      return False ;
  } else if( IS_ASCII_CHARA( pchara ) ){
    /* 一つ前の文字が "0123456789><?" の中のどれかなら送り仮名モード*
     * にはならない。*/
    if( pchara.chara >= '0' && pchara.chara <= '9' )
      return False ;
    if( pchara.chara == '>' || pchara.chara == '<' ||
	pchara.chara == '?' )
      return False ;
  }
  return True ;
}

/*
 * 超適当な変換ルールをひく関数。
 */
static struct skk_rom_kana_rule *j_assoc_rule
( struct myChar *prefix )
{
  struct skk_rom_kana_rule *element = skkinput_rom_kana_rule_list ;
  /* 何も設定されてなかったら、ひけない。*/
  if( element == NULL )
    return NULL ;
  /* じゃあ、探しましょう。*/
  while( element->state != NULL ){
    if( !myCharStrcmp( element->state, prefix ) )
      return element ;
    element ++ ;
  }
  return NULL ;
}

/*
 * 指定された文字列が撥音なのかどうか判断する関数。
 */
static int is_hatsuon_ok
( struct myChar *j_prefix, int rchara )
{
  int lchara, schara ;
  /* 大文字と小文字に変換した時にどうなるのかを入れておく。*/
  lchara = ( rchara >= 'a' && rchara <= 'z' )?
    rchara + ( 'A' - 'a' ) : rchara ;
  schara = ( rchara >= 'A' && rchara <= 'Z' )?
    rchara + ( 'a' - 'A' ) : rchara ;
  /* アルファベットが続かないで、何が撥音ですかい。*/
  if( schara < 'a' || schara > 'z' )
    return False ;
  /* "X" っていうことはないと思うけどね。*/
  if( schara == 'x' )
    return False ;
  /* 二文字じゃなかったら、駄目でしょ？ */
  if( !IS_END_OF_STRING( j_prefix[ 1 ] ) )
    return False ;
  if( !IS_ASCII_EQUAL( j_prefix[ 0 ], lchara ) &&
      !IS_ASCII_EQUAL( j_prefix[ 0 ], schara ) )
    return False ;
  return True ;
}

/*
 * カーソル位置に文字列を挿入する関数。
 */
static int j_insert_str
( struct SKKInputNode *node, int pos, struct myChar *str )
{
  int len_txt = myCharStrlen( node->textbuffer ) ;
  int len_str = myCharStrlen( str ) ;
  int count ;
  struct myChar *sptr, *dptr ;

  /* 挿入する文字列が存在しない場合の処理。*/
  if( str == NULL )
    return 0 ;

  /* 指定の場所から文字列を挿入すると、途中でバッファの容量を超える場合。*/
  if( pos + len_str > TEXTMAXLEN ){
    memccpy
      ( node->textbuffer + pos, str,
	sizeof( struct myChar ), TEXTMAXLEN - pos ) ;
    MYCHAR_SET_END_OF_STRING( node->textbuffer[ TEXTMAXLEN ] ) ;
    /* この場合、カーソル位置は常に \0 でないといけない。*/
    move_marker_ByInsert( node, pos, TEXTMAXLEN - pos ) ;
    /* cur_pos = TEXTMAXLEN ; */
    return 0 ;
  }
  if( pos != len_txt ){
    if( len_txt + len_str > TEXTMAXLEN ){
      /* 挿入する文字列でトータルの文字数が文字バッファの容量を超える場合。*/
      count = TEXTMAXLEN - len_str - pos ;
      sptr  = node->textbuffer + TEXTMAXLEN - len_str ;
      dptr  = node->textbuffer + TEXTMAXLEN ; /* = '\0' */
    } else {
      /* 挿入する文字列で文字バッファがあふれない場合。*/
      count = len_txt - pos ;
      sptr  = node->textbuffer + len_txt ;
      dptr  = node->textbuffer + len_txt + len_str ;
    }
    /* 挿入される部分の文字列を移動させる。*/
    MYCHAR_SET_END_OF_STRING( *sptr ) ;
    while( count >= 0 ){
      *dptr -- = *sptr -- ;
      count -- ;
    }
    /* 文字列の挿入を行う。*/
    dptr = node->textbuffer + pos ;
    sptr = str ;
    while( !IS_END_OF_STRING( *sptr ) )
      *dptr ++ = *sptr ++ ;
  } else {
    /* 文字列の挿入を行う。*/
    dptr = node->textbuffer + pos ;
    sptr = str ;
    while( !IS_END_OF_STRING( *sptr ) )
      *dptr ++ = *sptr ++ ;
    MYCHAR_SET_END_OF_STRING( *dptr ) ;
  }
  /* 挿入した文字列分だけマーカーを移動させる。*/
  move_marker_ByInsert( node, pos, len_str ) ;
  return 0 ;
}

/*
 * 指定された位置に文字を挿入し、マーカーを移動させる関数。
 */
int j_insert_myChara
( struct SKKInputNode *node, int pos, struct myChar wchara )
{
  int i ;
  int len = myCharStrlen( node->textbuffer ) ;
  struct myChar *ptr ;

  /* カーソルはテキストの一番最後を指しているのかな？ */
  if( IS_END_OF_STRING( node->textbuffer[ pos ] ) ){
    if( pos < TEXTMAXLEN ){
      /* いくらなんでも一番最後に足したぐらいではマーカーは移動しないだろう。*/
      node->textbuffer[ pos ++ ] = wchara ;
      MYCHAR_SET_END_OF_STRING( node->textbuffer[ pos ] ) ;
      node->cur_pos ++ ;
    } else {
      MYCHAR_SET_END_OF_STRING( node->textbuffer[ TEXTMAXLEN ] ) ;
    }
    return 0 ;
  }
  if( len >= TEXTMAXLEN ){
    len = TEXTMAXLEN ;
    ptr = node->textbuffer + TEXTMAXLEN ;
  } else {
    ptr = node->textbuffer + len + 1 ;
  }
  MYCHAR_SET_END_OF_STRING( *ptr ) ;
  ptr -- ;
  for( i = len ; i > pos ; i -- ){
    *ptr = *( ptr - 1 ) ;
    ptr -- ;
  }
  *ptr = wchara ;
  /* マーカーの移動。*/
  move_marker_ByInsert( node, pos, 1 ) ;
  /* cur_pos ++ ; */
  return 0 ;
}

/*
 * 指定された位置に文字を挿入し、マーカーを移動させる関数。
 */
int j_insert_chara
( struct SKKInputNode *node, int pos, int chara )
{
  struct myChar wchara ;

  if( chara & 0x80 ){
    /* まだ何も考えてないけど、直接入力なんてアリなの？ */
    return 0 ;
  } else {
    wchara.charset = CHARSET_ASCII ;
    wchara.chara   = chara ;
  }
  return j_insert_myChara( node, pos, wchara ) ;
}

/*
 * 入力された文字列が存在し得る prefix であれば、True を、そうでなければ
 * False を返す関数。
 */
static int j_kana_prefix_check
( struct myChar *str, int chara )
{
  int i, length = myCharStrlen( str ) ;

  for( i = 0 ; skk_prefix_list[ i ] != NULL ; i ++ ){
    /* 正しいプレフィクスなのでしょうか？ */
    if( !myCharCharStrncmp( str, skk_prefix_list[ i ], length ) ){
      /* 丁度だった最後に入力された文字を除いたのと一致する場合には *
       * 無視する。*/
      if( ( skk_prefix_list[ i ] )[ length + 0 ] != chara )
	continue ;
      /* 今回入力された文字を加えて…丁度じゃなかったら駄目だい！ */
      if( ( skk_prefix_list[ i ] )[ length + 1 ] != '\0' )
	continue ;
      return True ;
    }
  }
  return False ;
}

/*
 * j_prefix に入力された文字を足す関数。
 */
static int j_prefix_add
( struct myChar *j_prefix, int chara )
{
  struct myChar wchara ;
  /* まさか、こういうのが直接入力できるのでしょうか？ */
  if( chara & 0x80 )
    return False ;
  wchara.charset = CHARSET_ASCII ;
  wchara.chara   = chara ;
  while( j_prefix->chara != '\0' )
    j_prefix ++ ;
  *j_prefix ++ = wchara ;
  MYCHAR_SET_END_OF_STRING( *j_prefix ) ;
  return True ;
}

/*
 * 指定された領域を削除する関数。
 */
int j_delete_region
( struct SKKInputNode *node, int startpoint, int endpoint )
{
  struct myChar *sptr, *dptr ;

  if( startpoint == endpoint || startpoint > endpoint )
    return 0 ;

#ifdef DEBUG_LV1
  fprintf( stderr, "j-delete-region : \"" ) ;
#endif
  /* 指定領域の削除を行う。*/
  dptr = node->textbuffer + startpoint ;
  sptr = node->textbuffer + endpoint ;
  while( !IS_END_OF_STRING( *sptr ) ){
    *dptr ++ = *sptr ++ ;
  }
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
#ifdef DEBUG_LV1
  fprintf( stderr, "\"\n" ) ;
#endif
  /* マーカー(込み、カーソル)の移動。*/
  move_marker_ByDeleteRegion( node, startpoint, endpoint ) ;
  return 0 ;
}

/*
 * 指定された位置からカウント量だけ文字を抹消する関数。
 *-----
 * delete_region まがいのこともできるよ。
 */
static int delete_char
( struct SKKInputNode *node, int pos, int count )
{
  int i, length ;
  struct myChar *sptr, *dptr ;

  length = myCharStrlen( node->textbuffer ) ;
  i = length - ( pos + count ) ;
  if( i <= 0 ){
    MYCHAR_SET_END_OF_STRING( node->textbuffer[ pos ] ) ;
    /* 消去しようとしていた位置から右のマークは全て消去位置へ。*/
    set_marker_ByGeqPosition( node, pos, pos ) ;
    return 0 ;
  }
  dptr = node->textbuffer + pos ;
  sptr = node->textbuffer + pos + count ;
  while( i > 0 ){
    *dptr ++ = *sptr ++ ;
    i -- ;
  }
  if( IS_END_OF_STRING( *sptr ) )
    MYCHAR_SET_END_OF_STRING( *dptr ) ;
  /* マーカーの移動。*/
  move_marker_ByDeleteRegion( node, pos, pos + count ) ;
  return 0 ;
}

/*
 * Emacs-Lisp の Looking-At に相当する関数。
 *-----
 * 備考：特になし。何も言わんでくれ。
 */
static int looking_at
( struct SKKInputNode *node, int pos, struct myChar chara )
{
  /* どんな文字集合に属していても chara が等しくなかったら駄目。*/
  if( node->textbuffer[ pos ].chara != chara.chara )
    return False ;
  /* 同じ文字集合に属していれば、chara が等しければ良い。*/
  if( node->textbuffer[ pos ].charset == chara.charset )
    return True ;
  /* どちらも ASCII だったら、chara が等しければ良い。*/
  if( IS_ASCII_CHARA( chara ) &&
      IS_ASCII_CHARA( node->textbuffer[ pos ] ) )
    return True ;
  return False ;
}

int j_kakutei_kana_mode
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  if( !node->j_kana_mode )
    return False ;
  /* 改行キーなど、rom-kana-rule-list に入れたくない時に使う '\0' */
  if( chara != '\0' ){
    if( rom_kana_rule_list_check( node, chara ) ){
      /* 送り仮名入力の途中でヒットしたら、仮名の確定により変換開始 *
       * しなければならない。*/
      if( node->j_henkan_mode && !node->j_henkan_on &&
	  node->j_okurigana_mode ){
	j_set_okurigana( gw, sbuffer, node, node->j_okuri_chara ) ;
      }
      return True ;
    }
  }
  /* 最後に "n" が入っていて、それが変な入力で "ん"になったら？ */
  if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'n' ) &&
      IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
    j_n_convert_to_kana( node ) ;
    node->j_kana_mode = False ;
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
    if( node->j_henkan_mode && !node->j_henkan_on &&
	node->j_okurigana_mode ){
      struct myChar chara ;
      MYCHAR_SET_CHARA( chara, 'n' ) ;
      j_set_okurigana( gw, sbuffer, node, chara ) ;
    }
    return False ;
  }
  j_delete_region( node, node->j_kana_start_point, node->cur_pos ) ;
  node->j_kana_mode = False ;
  MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
  return False ;
}

/*
 * 仮名漢字変換の確定作業を行う関数なのである。
 */
int j_kakutei
( Widget gw, struct SKKInputNode *node )
{
  int pos ;
  struct myChar j_toroku_string[ TEXTBUFSIZE ] ;
  struct myChar chara ;

  node->j_mode    = True ;
  node->j_zenkaku = False ;
  node->j_abbrev  = False ;
  node->j_henkan_show_candidate_mode = False ;

  MYCHAR_SET_END_OF_STRING( node->j_num_list[ 0 ] ) ;

  /* 変換モードに入っているのかどうかを確認する。元のソースとは active と *
   * on の解釈が逆になっている。                                          */
  if( node->j_henkan_mode ){
    if( node->j_henkan_on && ( node->j_henkan_count > 0 ) ){
      /* ここで、辞書の更新がなされるが...うまく動くかな？ */
      if( node->j_current_henkan_vector_index != NULL ){
	struct myChar *henkankey = ( node->j_okurigana_mode )? 
	  node->j_henkan_key : node->j_search_key ; 
	/* そのまま登録されてしまうとまずい人達がいるかもしれないので *
	 * 修正する。*/
	copyCandidate
	  ( j_toroku_string,
	    node->j_current_henkan_vector_index->node,
	    node->j_current_henkan_vector_index->position,
	    node->j_current_henkan_vector_index->length ) ;
	if( j_quote_char( j_toroku_string ) ){
	  /* 文字列の修正があったから、仕方ないからつなぎかえ登録じゃ *
	   * なくて文字列登録する。*/
	  add_SudeniHenkanKakuteiShitamonoByString
	    ( henkankey, j_toroku_string, node->j_okurigana_mode ) ;
	  /* 登録した単語の数を一つ増やす。*/
	  skkinput_j_count_touroku ++ ;
	} else {
	  /* 文字列の修正がなかった場合には、この list がそのまま使え *
	   * るから…list つなぎかえ登録に走る。*/
	  add_SudeniHenkanKakuteiShitamono
	    ( henkankey,
	      &node->j_henkan_vector_index_top,
	      node->j_current_henkan_vector_index,
	      node->j_okurigana_mode ) ;
	}
	skkinput_j_count_kakutei ++ ;
      }
      /* 利用していたメモリの解放。*/
      free_VectorIndex( &( node->j_henkan_vector_index_top ) ) ;
      free_SkkinpSearchVector( &( node->j_henkan_vector_top ) ) ;
      node->j_current_henkan_vector_index = NULL ;
    }
    pos = node->j_henkan_start_point - 1 ;	/* goto-char */
    if( node->j_henkan_on ){
      MYCHAR_SET_JISX0208_1983( chara, 0x2227 /*▼*/ ) ;
      /* 変換開始位置に「▼」はあるのか？ 無かったら、変。*/
      if( looking_at( node, pos, chara ) ){
	delete_char( node, pos, 1 ) ;
      } else {
	err_msg( node, "It seems that you have deleted ▼." ) ;
      }
    } else {
      MYCHAR_SET_JISX0208_1983( chara, 0x2226 /*▽*/ ) ;
      if( looking_at( node, pos, chara ) ){
	delete_char( node, pos, 1 ) ;
      } else {
	err_msg( node, "It seems that you have deleted ▽." ) ;
      }
    }
    /* 確定時の動作…送り仮名モードだった場合の処理。*/
    if( node->j_okurigana_mode ){
      MYCHAR_SET_CHARA( chara, 0x2A /* "*" */ ) ;
      /* 送り仮名開始位置には "*" が存在した筈なんですけど…。*/
      pos = node->j_okurigana_start_point ;
      if( looking_at( node, pos, chara ) ){
	delete_char( node, pos, 1 ) ;
      }
      /* というわけで、送り仮名モードは停止になるわけです。*/
      node->j_okurigana_mode = False ;
      MYCHAR_SET_END_OF_STRING( node->j_okuri_chara ) ;
    }
    /* j_abbrev = False ; */
  }
  node->j_henkan_mode      = False ;
  node->j_henkan_on        = False ;
  node->j_okurigana_mode   = False ;
  MYCHAR_SET_END_OF_STRING( node->j_okuri_chara ) ;
  node->j_henkan_count     = 0 ;
  /* 変換モードではなくなったのだから、少なくとも変換開始位置と変換終 *
   * 了位置、送り仮名の位置はクリアされる。*/
  node->j_henkan_start_point    = -1 ;
  node->j_henkan_end_point      = -1 ;
  node->j_okurigana_start_point = -1 ;
  return 0 ;
}

/*
 * skkinput-rom-kana-rule-list を適用する関数。
 */
int rom_kana_rule_list_check
( struct SKKInputNode *node, int chara )
{
  struct skk_rom_kana_rule *element ;
  struct myChar wchara ;
  int length = myCharStrlen( node->j_prefix ) ;

  /* 漢字コードの直接入力なんてのはアリなのでしょうか？ */
  if( chara & 0x80 )
    return False ;

  /* 内部キャラに変換する。*/
  MYCHAR_SET_CHARA( wchara, chara ) ;

  /* 今回の入力文字を加えます。*/
  node->j_prefix[ length + 0 ] = wchara ;
  MYCHAR_SET_END_OF_STRING( node->j_prefix[ length + 1 ] ) ;
  /* まず現在のプレフィクスでテーブルをひきます。*/
  element = j_assoc_rule( node->j_prefix ) ;
  /* 何かヒットしましたか？ */
  if( element == NULL ){
    /* j-prefix は元に戻しておきませう。*/
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ length + 0 ] ) ;
    return False ;
  }
  /* 仮名プレフィクスを削除する。*/
  j_delete_region
    ( node, node->j_kana_start_point, node->cur_pos ) ;
  MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
  /* 仮名モードではなくなる…。*/
  node->j_kana_mode = False ;
  /* ヒットした文字列を挿入する。*/
  if( node->j_katakana_mode ){
    if( element->koutput != NULL )
      j_insert_str( node, node->cur_pos, element->koutput ) ;
  } else {
    if( element->houtput != NULL )
      j_insert_str( node, node->cur_pos, element->houtput ) ;
  }
  if( element->next != NULL ){
    /* 次状態が存在するなら、それを代入する。*/
    node->j_kana_start_point = node->cur_pos ;
    j_insert_str( node, node->cur_pos, element->next ) ;
    myCharStrcpy( node->j_prefix, element->next ) ;
    node->j_kana_mode = True ;
  }
  /* 適用できたということは仮名の確定があったから、変換開始その他を *
   * 意味するとする。*/
  return True ;
}

/*
 * 小文字アルファベットが入力されようとしている場合の処理を行う関数。
 *----
 * 小文字アルファベットの入力、それは仮名を入力しようとしている瞬間を意味
 * するのである。
 */
int j_kana_input
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  /* skkinput の内部状態を抜き出す。*/
  int status = ( node->j_henkan_mode << 3 ) | ( node->j_henkan_on << 2 ) |
    ( node->j_kana_mode << 1 ) | ( node->j_okurigana_mode ) ;
  /* int length ;*/
  struct myChar lchara ;

#ifdef DEBUG_LV0
  fprintf( stderr, "K( j_henkan_mode, j_henkan_on, j_kana_mode,\
j_okurigana_mode ) = (%d,%d,%d,%d)\n", node->j_henkan_mode, 
	   node->j_henkan_on, node->j_kana_mode, node->j_okurigana_mode ) ;
#endif
  if( chara == 'o' ){
    j_insert_o( gw, sbuffer, node, chara ) ;
    MYCHAR_SET_CHARA( node->j_prefix[ 0 ], 'o' ) ;
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
    node->j_kana_mode   = True ;
    /* "o" を入れた後のカーソル位置が仮名の開始位置になる。うーん。*/
    node->j_kana_start_point = node->cur_pos ;
    return 0 ;
  }
  /* 状態遷移表。*/
  switch( status ){
    /* 変換が行われている場合で、仮名の入力がなく、送り仮名が入っている場合。*/
  case J_HENKAN_MODE_ON | J_HENKAN_ON | J_KANA_MODE_OFF | J_OKURIGANA_ON :
    /* 送り仮名変換によって変換が行われた直後に、文字の入力があっ *
     * た場合。まず、確定してから、入力された文字を表示する。*/
    j_kakutei( gw, node ) ;
    goto j_kana_input_label1 ;

    /* 変換が行われている場合で、仮名の入力がない場合。*/
  case J_HENKAN_MODE_ON | J_HENKAN_ON | J_KANA_MODE_OFF | J_OKURIGANA_OFF :
    /* 確定作業が行われる。後は全部 OFF の場合と同じ。*/
    j_kakutei( gw, node ) ;

  case J_HENKAN_MODE_ON | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_ON :

    /* ▽が画面に出ている時の仮名入力。でも、こいつは...確定は抜きなのだ *
     * から、J_HENKAN_MODE_OFF の時と同じ。J_HENKAN_MODE 自体は変化  *
     * しないし、大丈夫でしょう。                                        */
  case J_HENKAN_MODE_ON | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_OFF :

    /* 何も入力されていない状態からの仮名入力。*/
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_OFF :
    j_kana_input_label1 : ;
    /* カーソル位置に仮名変換開始マークを設定する。*/
    node->j_kana_start_point = node->cur_pos ;
    /* 入力したキャラクタを表示する。ついでに、カーソルも移動してもら*
     * う。*/
    j_insert_chara( node, node->cur_pos, chara ) ;
    /* 仮名が開始したら、j-prefix も有効になる…そのタイミングだけで *
     * 良いの？ */
    j_prefix_add( node->j_prefix, chara ) ;
    /* 仮名モードがオンになる。*/
    node->j_kana_mode = True ;
    break ;

    /* 「▽た*っs」というような状態である。変換モードであり、送り仮名モード *
     * であり、仮名モードであるというのは。                                 */
  case J_HENKAN_MODE_ON  | J_HENKAN_OFF | J_KANA_MODE_ON  | J_OKURIGANA_ON :
#if 0
    /* 変換が行われている場合で、仮名prefix入力、送り仮名入力のある場合。*/
  case J_HENKAN_MODE_ON  | J_HENKAN_ON  | J_KANA_MODE_OFF | J_OKURIGANA_ON :
    /* この場合は確定してはいけない。送り仮名モードがついているということは *
     * 送り仮名が何なのかが確定していないということだから。                 */
#endif
    /* 変換モードであり、既に仮名が入力されている場合。*/
  case J_HENKAN_MODE_ON  | J_HENKAN_OFF | J_KANA_MODE_ON  | J_OKURIGANA_OFF :

    /* 仮名を入力しようとしている場合。*/
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_ON  | J_OKURIGANA_OFF :
    /* ルールリストをひきます。ここで、j-prefix は更新されます。*/
    if( rom_kana_rule_list_check( node, chara ) ){
      /* 送り仮名の入力の途中であり、この 'n' の入力によって送り *
       * 仮名であった 'n' が確定してしまう場合の処理。*/
      if( node->j_henkan_mode && node->j_okurigana_mode )
	j_set_okurigana( gw, sbuffer, node, node->j_okuri_chara ) ;
      break ;
    }
    if( chara == 'y' ) {
      if( !myCharCharStrcmp( node->j_prefix, "oh" ) &&
         IS_ASCII_EQUAL( node->textbuffer[ node->j_kana_start_point ], 'h' ) ){
        MYCHAR_SET_CHARA( node->j_prefix[ 0 ], 'h' ) ;
        MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
      }
    }
    /* 果たして、そんな prefix は存在できますの？ もりやさん。*/
    if( !j_kana_prefix_check( node->j_prefix, chara ) ){
      /* やっぱり、存在できませんのね。思った通りですわ。*/
      if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'n' ) && 
	  IS_END_OF_STRING( node->j_prefix[ 1 ] ) ) {
	/* 仮名開始位置に表示されている「n」を消す。*/
	j_n_convert_to_kana( node ) ;
	/* 仮名入力開始位置をずらす。*/
	node->j_kana_start_point = node->cur_pos ;
	/* 今回入力した文字をカーソル位置に代入する。*/
	j_insert_chara( node, node->cur_pos, chara ) ;
	/* 仮名が開始したら、j-prefix も有効になる…そのタイミング *
	 * だけで良いの？ */
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
	/* j_kana_mode = True */
	break ;
      }
      if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'o' ) && 
	  IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
	if( chara == 'h' ){
	  node->j_kana_mode = True ;
	  /* 仮名の入力位置をずらしました、と。*/
	  node->j_kana_start_point = node->cur_pos ;
	  /* 今回入力した文字をカーソル位置に代入する。*/
	  j_insert_chara( node, node->cur_pos, chara ) ;
	  j_prefix_add( node->j_prefix, chara ) ;
	  break ;
	} else {
	  /* 以前に "o" を入力していたという情報は消す。*/
	  MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
	  goto j_kana_input_label3 ;
	}
      }
      /* "oh" のチェックを行う。*/
      if( !myCharCharStrcmp( node->j_prefix, "oh" ) &&
	  IS_ASCII_EQUAL
	  ( node->textbuffer[ node->j_kana_start_point ], 'h' ) ){
	delete_char( node, node->j_kana_start_point, 1 ) ;
	if( node->j_katakana_mode ){
	  MYCHAR_SET_JISX0208_1983( lchara, 0x252a /*"オ"*/ ) ;
	} else {
	  MYCHAR_SET_JISX0208_1983( lchara, 0x242a /*"お"*/ ) ;
	}
	j_insert_myChara( node, node->j_kana_start_point, lchara ) ;
	/* 仮名の入力位置をずらしました、と。*/
	node->j_kana_start_point = node->cur_pos ;
	/* 今回入力した文字をカーソル位置に代入する。*/
	j_insert_chara( node, node->cur_pos, chara ) ;
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
	break ;
      }
      /* 撥音かどうかをチェックするのである。*/
      if( is_hatsuon_ok( node->j_prefix, chara ) ){
	/* そこに表示されている文字を消しましょう。*/
	delete_char( node, node->j_kana_start_point, 1 ) ;
	/* そこに促音便を代入しましょう。*/
	/* このとき、cur_pos == j_kana_start_point の筈である。*/
	if( node->j_katakana_mode ){
	  MYCHAR_SET_JISX0208_1983( lchara, 0x2543 /*"ッ"*/ ) ;
	} else {
	  MYCHAR_SET_JISX0208_1983( lchara, 0x2443 /*"っ"*/ ) ;
	}
	j_insert_myChara( node, node->j_kana_start_point, lchara ) ;
	/* 仮名の入力位置をずらしました、と。*/
	node->j_kana_start_point = node->cur_pos ;
	/* 今回入力した文字をカーソル位置に代入する。*/
	j_insert_chara( node, node->cur_pos, chara ) ;
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
	/* j_kana_mode = True */
	break ;
      }
      /* 要らない prefix は消去しましてよ。*/
      delete_char( node, node->j_kana_start_point,
		   node->cur_pos - node->j_kana_start_point ) ;
      /* 仮名の変換開始位置も変更しますからね。*/
      node->j_kana_start_point = node->cur_pos ;
      /* 今回入力した文字に修正します。*/
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
    }
    goto j_kana_input_label3 ;

    /* げげげげげげ…どうする、どうなる、阪本崇！
     * どうやって解決するのだ！？ */
  case J_HENKAN_MODE_ON  | J_HENKAN_ON  | J_KANA_MODE_ON  | J_OKURIGANA_ON :
    /* 送り仮名変換によって変換が行われた直後に、文字の入力があった場合。*
     * まず、確定してから、入力された文字を表示する。*/
#if 0
    /* まず、途中で一回仮名の確定があったかどうかを判断する？ しなくて
       良いみたいだから、ここはコンパイルが通らないようにしておく。*/
    if( ( node->j_okurigana_start_point + 1 ) <
	node->j_kana_start_point ){
      j_kakutei( gw, node ) ;
      goto j_kana_input_label2 ;
    }
#endif
    /* ルールリストをひきます。ここで、j-prefix は更新されます。*/
    if( rom_kana_rule_list_check( node, chara ) ){
      break ;
    }
    /* 果たして、そんな prefix は存在できますの？ もりやさん。*/
    if( !j_kana_prefix_check( node->j_prefix, chara ) ){
      if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'o' ) &&
	  IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
	if( chara == 'h' ){
	  node->j_kana_mode = True ;
	  /* 仮名の入力位置をずらしました、と。*/
	  node->j_kana_start_point = node->cur_pos ;
	  /* 今回入力した文字をカーソル位置に代入する。*/
	  j_insert_chara( node, node->cur_pos, chara ) ;
	  j_prefix_add( node->j_prefix, chara ) ;
	  break ;
	} else {
	  /* 確定の場所はここ？ */
	  j_kakutei( gw, node ) ;
	  /* 以前に "o" を入力していたという情報は消す。*/
	  MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
	  goto j_kana_input_label3 ;
	}
      }
      /* 確定の場所はここ？ */
      j_kakutei( gw, node ) ;
      if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'n' ) && 
	  IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
	/* 仮名開始位置に表示されている「n」を消す。*/
	j_n_convert_to_kana( node ) ;
	/* 仮名入力開始位置をずらす。*/
	node->j_kana_start_point = node->cur_pos ;
	/* 今回入力した文字をカーソル位置に代入する。*/
	j_insert_chara( node, node->cur_pos, chara ) ;
	/* 仮名が開始したら、j-prefix も有効になる…そのタイミング *
	 * だけで良いの？ */
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
	/* j_kana_mode = True */
	break ;
      }
      /* "oh" のチェックを行う。*/
      if( !myCharCharStrcmp( node->j_prefix, "oh" ) &&
	  IS_ASCII_EQUAL( node->textbuffer[ node->j_kana_start_point ], 
			  'h' ) ){
	delete_char( node, node->j_kana_start_point, 1 ) ;
	if( node->j_katakana_mode ){
	  MYCHAR_SET_JISX0208_1983( lchara, 0x252a /*"オ"*/ ) ;
	} else {
	  MYCHAR_SET_JISX0208_1983( lchara, 0x242a /*"お"*/ ) ;
	}
	j_insert_myChara( node, node->j_kana_start_point, lchara ) ;
	/* 仮名の入力位置をずらしました、と。*/
	node->j_kana_start_point = node->cur_pos ;
	/* 今回入力した文字の表示。*/
	j_insert_chara( node, node->cur_pos, chara ) ;
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
	/* node->j_kana_mode = True ;*/
	break ;
      }
      /* 撥音かどうかをチェックするのである。*/
      if( is_hatsuon_ok( node->j_prefix, chara ) ){
	/* そこに表示されている文字を消しましょう。*/
	delete_char( node, node->j_kana_start_point, 1 ) ;
	/* そこに促音便を代入しましょう。*/
	/* このとき、cur_pos == j_kana_start_point の筈である。*/
	if( node->j_katakana_mode ){
	  MYCHAR_SET_JISX0208_1983( lchara, 0x2543 /*"ッ"*/ ) ;
	} else {
	  MYCHAR_SET_JISX0208_1983( lchara, 0x2443 /*"っ"*/ ) ;
	}
	j_insert_myChara( node, node->j_kana_start_point, lchara ) ;
	/* 仮名の入力位置をずらしました、と。*/
	node->j_kana_start_point = node->cur_pos ;
	/* 今回入力した文字をカーソル位置に代入する。*/
	j_insert_chara( node, node->cur_pos, chara ) ;
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
	/* j_kana_mode = True */
	break ;
      }
      /* 要らない prefix は消去しましてよ。*/
      delete_char( node, node->j_kana_start_point,
		   node->cur_pos - node->j_kana_start_point ) ;
      /* 仮名の変換開始位置も変更しますからね。*/
      node->j_kana_start_point = node->cur_pos ;
      /* 今回入力した文字に修正します。*/
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
    } else {
      j_kakutei( gw, node ) ;
    }
  j_kana_input_label3:
    /* 今回入力した文字の表示。*/
    j_insert_chara( node, node->cur_pos, chara ) ;
    j_prefix_add( node->j_prefix, chara ) ;
    break ;

    /* 以下、あっちゃいけない状態を列挙かな？ */
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_ON :
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_ON  | J_OKURIGANA_ON :

    /* ここの所は、変換モードになっていないのに、変換されているから。*/
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_OFF | J_OKURIGANA_OFF :
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_OFF | J_OKURIGANA_ON :
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_ON  | J_OKURIGANA_OFF :
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_ON  | J_OKURIGANA_ON :

  default :
    /* この事態はありえないので、エラー。*/
    err_msg( node, "ERROR : Illegal status %d\n", status ) ;
    break ;
  }
  return 0 ;
}

/*
 * マーカを変化させる関数。
 *-----
 * 入力はマーカの位置です。そこにマーカが無かったら、エラーを返します。
 */
static int j_change_marker( struct SKKInputNode *node, int pos )
{
  if( node->textbuffer[ pos ].charset != CHARSET_JISX0208_1983 ||
      node->textbuffer[ pos ].chara != 0x2226 /*▽*/ )
    return True ;
  MYCHAR_SET_JISX0208_1983( node->textbuffer[ pos ], 0x2227 /*▼*/ ) ;
  return False ;
}

/*
 * マーカを変化させる関数。
 *-----
 * 入力はマーカの位置です。そこにマーカが無かったら、エラーを返します。
 */
static int j_change_marker_to_write( struct SKKInputNode *node, int pos )
{
  if( node->textbuffer[ pos ].charset != CHARSET_JISX0208_1983 ||
      node->textbuffer[ pos ].chara != 0x2227 /*▼*/ )
    return True ;
  MYCHAR_SET_JISX0208_1983( node->textbuffer[ pos ], 0x2226 /*▽*/ ) ; 
  return False ;
}

/* Show candidates in the minibuffer */
static VectorIndex *j_henkan_show_candidates
( struct SKKInputNode *node, int *count )
{
  int i, restnum, mojisuu, h_count ;
  static char *j_henkan_show_candidates_keys[ 7 ] = {
    "A:", "S:", "D:", "F:", "J:", "K:", "L:",
  } ;
  VectorIndex *vNode ;
  /* うーん、ここにもこんなでっかいバッファが必要になっちゃった。(;_;) */
  struct myChar buffer[ TEXTBUFSIZE ] ;
  struct myChar *rptr ;

  vNode   = node->j_current_henkan_vector_index ;
  h_count = node->j_henkan_count ; 
  MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;

  /* 何個候補が残っているのかな？ */
  restnum = node->j_henkan_vector_index_length - h_count + 1 ;

  /* 最大表示可能文字数の設定、これ以上だとバッファがあふれる。*/
  mojisuu = TEXTMAXLEN ;

  /* 候補を列挙しませう。*/
  for( i = 0 ; i < 7 && vNode != NULL ; i ++, restnum -- ){
    /* 選択キーを付け加える。*/
    myCharCharStrncat
      ( node->mtextbuffer, j_henkan_show_candidates_keys[ i ],
	mojisuu ) ;
    mojisuu -= 2 ;
    if( mojisuu <= 0 )
      break ;

    /* 候補を付け加える。*/
    copyCandidate
      ( temporary_buffer, vNode->node, vNode->position, TEXTMAXLEN ) ;
    rptr = temporary_buffer ;
    if( !IS_END_OF_STRING( node->j_num_list[ 0 ] ) && 
	skkinput_use_numeric_conversion ){
      j_numeric_convert( node, temporary_buffer, buffer, TEXTBUFSIZE ) ;
      rptr = buffer ;
    }
    /* 次の候補を指すように設定する。*/
    vNode = vNode->next ;

    /* 何番目の候補かを勘定する。*/
    h_count ++ ;
    myCharStrncat( node->mtextbuffer, rptr, mojisuu ) ;
    mojisuu -= myCharStrlen( rptr ) ;
    if( mojisuu <= 0 )
      break ;
    /* 空白を加える。*/
    myCharCharStrncat( node->mtextbuffer, " ", mojisuu ) ;
    mojisuu -- ;
    if( mojisuu <= 0 )
      break ;
  }
  /* まだ文字バッファに余裕がありますか？ */
  if( mojisuu > 0 ){
    /* [残り %d] を加える。*/
    MYCHAR_SET_CHARA( temporary_buffer[ 0 ], '[' ) ;
    temporary_buffer[ 1 ].charset = CHARSET_JISX0208_1983 ;
    temporary_buffer[ 1 ].chara   = 0x3b44 ; /*残*/
    temporary_buffer[ 2 ].charset = CHARSET_JISX0208_1983 ;
    temporary_buffer[ 2 ].chara   = 0x246a ; /*り*/
    MYCHAR_SET_CHARA( temporary_buffer[ 3 ], ' ' ) ;
    myCharItoa( temporary_buffer + 4, restnum ) ;
    myCharCharStrcat( temporary_buffer, "]" ) ;
    myCharStrncat( node->mtextbuffer, temporary_buffer, mojisuu ) ;
  }
  /* 文字列として終端する。*/
  MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ TEXTMAXLEN ] ) ;
  node->j_henkan_show_candidate_mode = True ;

  /* 実際、どこまで候補を表示したかの勘定を返して… */
  if( count != NULL )
    *count = h_count ;

  /* ポインタも返す。*/
  return vNode ;
}

/*
 * 仮名漢字変換を開始する時に呼出されるコールバック関数。
 */
static void j_get_henkan_results
( struct SKKInputNode *node )
{
  SkkinpSearchVector *j_purge_vector_top ;
  VectorIndex *j_purge_vector_index_top ;
  struct myChar *henkankey ;

  /* 変換キーを選択する。*/
  henkankey = ( node->j_okurigana_mode )?
    node->j_henkan_key : node->j_search_key ;

  node->j_henkan_vector_top = NULL ;
  /* 内部に記憶されている辞書から検索を行う。*/
  node->j_henkan_vector_top = searchSudeniHenkanKakuteiShitamono
    ( henkankey, node->j_henkan_vector_top,
      node->j_okurigana_mode ) ;
  /* まず、局所辞書から検索を行う。*/
  node->j_henkan_vector_top = j_search_skkinput_local_jisyo
    ( henkankey, node->j_henkan_vector_top, node->j_okurigana_mode ) ;
  /* SKK からの返事はありましたか？ */
  node->j_henkan_vector_top = skkinput_Request
    ( henkankey, node->j_henkan_vector_top ) ;
  /* そして、どれか一つでも変換に成功した場合…。*/
  if( node->j_henkan_vector_top != NULL ){
    /* こっちはパージの検索。*/
    j_purge_vector_index_top = NULL ;
    j_purge_vector_top = NULL ;
    j_purge_vector_top = searchSudeniPurgeSaretamono
      ( henkankey, j_purge_vector_top,
	node->j_okurigana_mode ) ;
    /* 一旦ハッシュに現在パージされている候補を叩き込む。*/
    /* 切り出した後にハッシュがクリアされては困るので、False であ *
     * る。どれか一つでもヒットしなければ、パージリストを検索する *
     * 意味は全く無い。*/
    if( j_purge_vector_top != NULL ){
      int tmp ;
      j_purge_vector_index_top = makeSearchVectorIndex
	( j_purge_vector_top, &tmp, False ) ;
    }
    /* SKK からの返事を候補毎に切り出す。*/
    node->j_henkan_vector_index_top =
      makeSearchVectorIndex( node->j_henkan_vector_top,
			     &node->j_henkan_vector_index_length, True ) ;
    /* purge なんかの攻撃をかわし、きちんと切り出せるものがあった *
     * 時の処理。*/
    if( node->j_henkan_vector_index_top != NULL ){
      /* 変換回数の設定。*/
      node->j_henkan_count = 1 ;
      /* 一番先頭の候補を指すようにする。*/
      node->j_current_henkan_vector_index = 
	node->j_henkan_vector_index_top ;
    } else {
      /* purge の影響か…何か辞書から送ってきたんだけど、解析して *
       * みたら駄目だった、そんなの使っちゃいけなかった。*/
      free_SkkinpSearchVector( &node->j_henkan_vector_top ) ;
      node->j_current_henkan_vector_index = NULL ;
      /* 変換回数の設定。*/
      node->j_henkan_count = 0 ;
    }
    /* パージのための情報はもしかしてもう不要？ */
    free_VectorIndex( &j_purge_vector_index_top ) ;
    free_SkkinpSearchVector( &j_purge_vector_top ) ;
  }
  return ;
}

/*
 * 変換動作を行う関数。
 *-----
 * この関数に先駆けて j_start_henkan が呼ばれている必要がありますよ。
 */
static int j_henkan
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  int found = False ;

  /* これが最初の変換だった場合には、*/
  if( node->j_henkan_on != True ){
    /* 「▽」を「▼」に変換して... */
    if ( j_change_marker
	 ( node, node->j_henkan_start_point - 1 ) ){
      err_msg( node, "It seems that you have deleted ▽." ) ;
      node->j_henkan_mode    = False ;
      node->j_okurigana_mode = False ;
      node->j_kana_mode      = False ;
      return 0 ;
    }
    /* 変換中フラグを立てるのである。*/
    node->j_henkan_on    = True ;
    node->j_henkan_count = 0 ;
  }
  /* 変換キーも設定されてないのに変換しようとしたら、確定してしまう。*/
  if( !myCharStrlen( node->j_henkan_key ) ){
    j_kakutei( gw, node ) ;
    return 0 ;
  }
  /* これが最初の変換だった場合には Ｎｏ．２ */
  if( node->j_henkan_count == 0 ){
    /* 変換の開始要求を発行する。*/
    j_get_henkan_results( node ) ;

    /* この結果、j_henkan_vector その他に変換結果がきちんとリストになっ*
     * て入るのである。*/

    /* 変換の結果はどうだったのかな？ */
    if( node->j_henkan_count ){
      found = True ;
    } else {
      found = False ;
    }
  } else {
    if( node->j_henkan_count < 4 ){
      /* 本当は四回以上になったら、一覧表示に切り替わるとかある。*/
      if( ( node->j_current_henkan_vector_index )->next != NULL ){
	node->j_current_henkan_vector_index =
	  ( node->j_current_henkan_vector_index )->next ;
	node->j_henkan_count ++ ;
	found = True ;
      }
    } else {
      /* 四回以上になったら、一覧表示に切り替わる。*/
      if( ( node->j_current_henkan_vector_index )->next != NULL ){
	/* 四回以上になった瞬間に NULL とかいって、候補がもう無く *
	 * なっていることがないことを確認する。*/
	node->j_current_henkan_vector_index =
	  ( node->j_current_henkan_vector_index )->next ;

	/* これで５個めということになる。*/
	node->j_henkan_count ++ ;

	node->j_henkan_show_candidate_mode = True ;
	/* 変換キーを抹消します。*/
	j_delete_region
	  ( node, node->j_henkan_start_point, node->j_henkan_end_point ) ;
	/* 候補の一覧を表示します、と。*/
	j_henkan_show_candidates( node, NULL ) ;
	/* で、処理をいきなし抜けます。*/
	return 0 ;
      }
    }
  }
  if( found ){
#if 1
    if( node->j_henkan_vector_index_top == NULL ){
      fprintf( stderr, "found, but no candidates exist.\n" ) ;
      fflush( stderr ) ;
    } else {
#endif
      /* 変換キーを抹消します。*/
      node->cur_pos2 = node->j_kana_start_point ;
      node->cur_pos3 = node->j_okurigana_start_point ;
      j_delete_region
	( node, node->j_henkan_start_point, node->j_henkan_end_point ) ;
      copyCandidate
	( temporary_buffer, ( node->j_current_henkan_vector_index )->node,
	  ( node->j_current_henkan_vector_index )->position,
	  TEXTMAXLEN ) ;
      node->j_kana_start_point = node->cur_pos ;
      /* 変換終了位置を調整。*/
      node->cur_pos_backup = node->cur_pos ;
      node->cur_pos        = node->j_henkan_start_point ;
      /* 文字列の挿入。*/
#if 0
      j_insert_str( node, node->j_henkan_start_point, temporary_buffer ) ;
#else
      j_insert_word
	( node, node->j_henkan_start_point, temporary_buffer ) ;
#endif
      node->j_henkan_end_point = node->cur_pos ;
      node->j_kana_start_point = node->cur_pos2 ;
      node->j_okurigana_start_point = node->cur_pos3 ;
      node->cur_pos = node->cur_pos_backup ;
    }
  } else {
    /* minibuffer を開いての変換作業に移動する。*/
    skkinput_j_henkan_in_minibuff_init( gw, sbuffer, node ) ;
  }
  return 0 ;
}

/*
 * 大文字のアルファベットが入力されたことにより、変換開始位置が決定され
 * 変換モードへと移行するという処理を行う関数。
 * -----
 * ただし、母音の入力は全く考えていない。ここに入力されるのは子音だけで
 * あるということを理解して頂きたい。母音が入力された場合の状態遷移は別
 * の関数に記述されることになる。
 * -----
 * 子音の入力 ....
 *   1. 仮名入力がされている ... 仮名prefixのチェック、促音、「ん」のチェック
 *   2. ▽の表示と仮名入力,変換開始点を設定する。
 *   3. 送り仮名モードは必要か？
 */
static int j_set_henkan_point_ByConsonat
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  /* skkinput の内部状態を抜き出す。*/
  int status = ( node->j_henkan_mode << 3 ) | ( node->j_henkan_on << 2 ) |
    ( node->j_kana_mode << 1 ) | ( node->j_okurigana_mode ) ;
  int rchara, ret ;
  struct myChar tchara, lchara ;

#ifdef DEBUG_LV0
  fprintf( stderr, "C( j_henkan_mode, j_henkan_on, j_kana_mode,\
j_okurigana_mode ) = (%d,%d,%d,%d)\n", node->j_henkan_mode, 
	   node->j_henkan_on, node->j_kana_mode, node->j_okurigana_mode ) ;
#endif
  /* 前の文字も残しておく。*/
  rchara = chara ;
  /* 大文字、小文字の変換。*/
  if( chara >= 'A' && chara <='Z' )
    chara += 'a' - 'A' ;

  /* 状態遷移表。*/
  switch( status ){

    /* 何も入力されていない状態からの変換位置指定。*/
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_OFF :
    j_set_henkan_point_label1 : ;
    MYCHAR_SET_JISX0208_1983( lchara, 0x2226 /*"▽"*/ ) ;
    /* ▽を表示して。*/
    j_insert_myChara( node, node->cur_pos, lchara ) ;
    /* 変換開始位置をカーソル位置に決定して。*/
    node->j_henkan_start_point = node->cur_pos ;
    node->j_kana_start_point   = node->cur_pos ;
    /* 変換モード、仮名入力モードをオンにする。*/
    node->j_henkan_mode = node->j_kana_mode = True ;
    /* カーソル位置にキャラクタを表示する。*/
    j_insert_chara( node, node->cur_pos, chara ) ;
    MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
    break ;

    /* 変換中でないのに送り仮名モードになることはない。*/
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_ON :
    break ;

    /* 既に仮名モードがオンであった場合の処理。*/
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_ON | J_OKURIGANA_OFF :
    /* 最初に小文字が入ってから、次に大文字が入った場合…ルールリス *
     * トにヒットする場合には、変換開始にはならない。*/
    if( rom_kana_rule_list_check( node, rchara ) ){
      break ;
    }
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'o' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
      node->j_kana_mode = False ;
      /* この仮名モードは "o" によるものであったから、もともと存在し *
       * ないものとして扱う。*/
      goto j_set_henkan_point_label1 ;
    }
    /* 取り敢えず、変換モードへ移行する。*/
    node->j_henkan_mode = True ;
    /* 「("sk" "sk" ("". "")) という規則があった場合には、prfix check *
     * をなさってはいけませんわ、マスター。」*/
    if( !rom_kana_rule_list_check( node, chara ) ){
      /* この場合、prefix とか促音の処理とかをまずする。*/
      if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'n' ) &&
	  IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
	/* 仮名開始位置に表示されている「n」を消す。*/
	j_n_convert_to_kana( node ) ;
	/* ▽マークの挿入。*/
	MYCHAR_SET_JISX0208_1983( lchara, 0x2226 /*"▽"*/ ) ;
	j_insert_myChara( node, node->cur_pos, lchara ) ;
	/* 変換開始位置の設定。これは「▽」の位置にあわせる。*/
	node->j_henkan_start_point = node->cur_pos ;
	node->j_kana_start_point   = node->cur_pos ;
	j_insert_chara( node, node->cur_pos, chara ) ;
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
	node->j_kana_mode = True ;
	break ;
      }
      /* 促音かどうかをチェックするのである。*/
      if( IS_ASCII_EQUAL( node->textbuffer[ node->j_kana_start_point ],
			  chara ) && chara != 'x' ){
	/* そこに表示されている文字を消しましょう。*/
	delete_char( node, node->j_kana_start_point, 1 ) ;
	/* そこに促音便を代入しましょう。*/
	/* このとき、cur_pos == j_kana_start_point の筈である。*/
	if( node->j_katakana_mode ){
	  MYCHAR_SET_JISX0208_1983( lchara, 0x2543 /*"ッ"*/ ) ;
	} else {
	  MYCHAR_SET_JISX0208_1983( lchara, 0x2443 /*"っ"*/ ) ;
	}
	j_insert_myChara( node, node->j_kana_start_point, lchara ) ;
	/* ▽マークの挿入。*/
	MYCHAR_SET_JISX0208_1983( lchara, 0x2226 /*"▽"*/ ) ;
	j_insert_myChara( node, node->j_kana_start_point, lchara ) ;
	node->j_henkan_start_point =
	  node->j_kana_start_point + 1 ;
	/* 仮名の入力位置をずらしました、と。*/
	node->j_kana_start_point = node->cur_pos ;
	/* 今回入力した文字をカーソル位置に代入する。*/
	j_insert_chara( node, node->cur_pos, chara ) ;
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
#if 0
	fprintf( stderr, "(c,k,h) = ( %d, %d, %d )\n", node->cur_pos,
		 node->j_kana_start_point, node->j_henkan_start_point ) ;
#endif
	/* j_kana_mode = True */
	break ;
      }
      /* 果たして、そんな prefix は存在できますの？ もりやさん。*/
      if( !j_kana_prefix_check( node->j_prefix, chara ) ){
	/* やっぱり、存在できませんのね。思った通りですわ。*/
	/* 要らない prefix は消去しましてよ。*/
	delete_char( node, node->j_kana_start_point, 
		     node->cur_pos - node->j_kana_start_point ) ;
	/* 仮名の変換開始位置も変更しますからね。*/
	node->j_kana_start_point = node->cur_pos ;
	/* 今回入力した文字の表示。*/
	j_insert_chara( node, node->cur_pos, chara ) ;
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
      } else {
	/* 今回入力した文字の表示。*/
	j_insert_chara( node, node->cur_pos, chara ) ;
	j_prefix_add( node->j_prefix, chara ) ;
      }
    }
    /* ▽マークの挿入。*/
    MYCHAR_SET_JISX0208_1983( lchara, 0x2226 /*"▽"*/ ) ;
    j_insert_myChara( node, node->j_kana_start_point, lchara ) ;
    /* 変換開始位置の設定。これは「▽」の位置にあわせる。*/
    node->j_henkan_start_point =
      node->j_kana_start_point + 1 ;
    node->j_kana_start_point   =
      node->j_henkan_start_point ; /* 2 == strlen( "▽" ) */
    break ;

    /* 変換中でないのに送り仮名モードになることはない。*/
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_ON  | J_OKURIGANA_ON :

    /* 変換モードに移行していないのに、変換中であることはない。*/
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_OFF | J_OKURIGANA_OFF :
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_OFF | J_OKURIGANA_ON  :
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_ON  | J_OKURIGANA_OFF :
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_ON  | J_OKURIGANA_ON  :
    break ;
    
  case J_HENKAN_MODE_ON  | J_HENKAN_OFF | J_KANA_MODE_ON  | J_OKURIGANA_OFF :
    if( rom_kana_rule_list_check( node, rchara ) ){
      break ;
    }
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'o' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
      node->j_kana_mode = False ;
      /* この仮名モードは "o" によるものであったから、もともと存在し *
       * ないものとして扱う。*/
      goto j_set_henkan_point_label2 ;
    }
    /* この場合、prefix とか促音の処理とかをまずする。*/
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'n' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
      if( node->j_henkan_start_point == node->j_kana_start_point ){
	/* 仮名開始位置に表示されている「n」を消す。*/
	j_n_convert_to_kana( node ) ;
	if( chara == 'n' ){
	  node->j_kana_mode = False ;
	  MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
	} else {
	  /* カーソル位置の設定。*/
	  node->j_kana_start_point = node->cur_pos ;
	  /* 仮名入力モードへ移行。*/
	  node->j_kana_mode = True ;
	  /* 今回入力した文字を表示する。*/
	  j_insert_chara( node, node->cur_pos, chara ) ;
	  MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	  MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
	}
      } else {
	/* 仮名開始位置に表示されている「n」を消す。*/
	j_n_convert_to_kana( node ) ;
	/* カーソル位置が送り仮名の開始ポイント。*/
	node->j_okurigana_start_point = node->cur_pos ;
	/* 送り仮名の開始ポイントに「*」を挿入する。*/
	j_insert_chara( node, node->cur_pos, '*' ) ;
	/* 送り仮名モードへ移行する。*/
	node->j_okurigana_mode = True ;
	MYCHAR_SET_CHARA( node->j_okuri_chara, chara ) ;
	/* カーソル位置の設定。*/
	node->j_kana_start_point = node->cur_pos ;
	/* 仮名入力モードへ移行。*/
	node->j_kana_mode = True ;
	/* 今回入力した文字を表示する。*/
	j_insert_chara( node, node->cur_pos, chara ) ;
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
      }
      break ;
    }
    /* 入力が "n" であった場合の処理。*/
    if( chara == 'n' ){
      /*
       * この場合は既に前で実行されているので、ここではいらない。
       * if( textbuffer[ j_kana_start_point ] == 'n' ){
       */
      delete_char( node, node->j_kana_start_point, 1 ) ;
      /* カーソル位置の設定。*/
      node->j_kana_start_point = node->cur_pos ;
      /* 仮名入力モードへ移行。*/
      node->j_kana_mode = True ;
      /* 今回入力した文字を表示する。*/
      j_insert_chara( node, node->cur_pos, chara ) ;
      MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
      break ;
    }
    /* 促音かどうかをチェックするのである。*/
    if( IS_ASCII_EQUAL
	( node->textbuffer[ node->j_kana_start_point ], chara ) && 
	chara != 'x' ){
      /* そこに表示されている文字を消しましょう。*/
      delete_char( node, node->j_kana_start_point, 1 ) ;
      /* そこに促音便を代入しましょう。*/
      /* このとき、cur_pos == j_kana_start_point の筈である。*/
      if( node->j_katakana_mode ){
	MYCHAR_SET_JISX0208_1983( lchara, 0x2543 /*"ッ"*/ ) ;
      } else {
	MYCHAR_SET_JISX0208_1983( lchara, 0x2443 /*"っ"*/ ) ;
      }
      j_insert_myChara( node, node->j_kana_start_point, lchara ) ;

      /* カーソル位置が送り仮名の開始ポイント。*/
      node->j_okurigana_start_point = node->cur_pos ;
      /* 送り仮名の開始ポイントに「*」を挿入する。*/
      j_insert_chara( node, node->cur_pos, '*' ) ;
      /* 送り仮名モードへ移行する。*/
      node->j_okurigana_mode = True ;
      MYCHAR_SET_CHARA( node->j_okuri_chara, chara ) ;
      /* カーソル位置の設定。*/
      node->j_kana_start_point = node->cur_pos ;
      /* 今回入力した文字を表示する。*/
      j_insert_chara( node, node->cur_pos, chara ) ;
      MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
      /* j_kana_mode = True */
      break ;
    }
    /* 「rom-kana-rule-list を引いてしまったら、j-prefix は初期化さ *
     *   れてしまうかもしれませんわ。」*/
    /* 「てぇっと、いうことはあらかじめ覚えておけばいいってことだな」*/
    /* 「ええ、小樽さま」*/
    tchara = node->j_prefix[ 0 ] ;
    /* 「("sk" "sk" ("". "")) という規則があった場合には、prfix check *
     * をなさってはいけませんわ」*/
    if( rom_kana_rule_list_check( node, chara ) ){
      /* 「悪い人達ですわ。あんなにお願いしましたのに…。QskK なんて *
       *   入力をなさるなんて…」 */
      node->cur_pos_backup = node->cur_pos ;
      node->cur_pos = node->j_kana_start_point ;
      ret = is_okurigana_start_ok( node ) ;
      node->cur_pos = node->cur_pos_backup ;
      /* 「ほらほら、ねえねえ。"QskK" で "▼Ｓえすけいけい" って出た *
       *   よ。見て見て…」 */
      /* 「負けませんわ〜！」(それはバグだし…) */
      if( ret ){
	/* カーソル位置が送り仮名の開始ポイント。*/
	node->j_okurigana_start_point = node->j_kana_start_point ;
	node->cur_pos2 = node->j_kana_start_point ;
	/* 送り仮名の開始ポイントに「*」を挿入する。*/
	j_insert_chara( node, node->j_okurigana_start_point, '*' ) ;
	node->j_kana_start_point = node->cur_pos2 ;
	/* 送り仮名モードへ移行する。*/
	node->j_okurigana_mode = True ;
	node->j_okuri_chara = tchara ;
	j_set_okurigana( gw, sbuffer, node, node->j_okuri_chara ) ;
      }
      break ;
    }
    /* 果たして、そんな prefix は存在できますの？ もりやさん。*/
    if( !j_kana_prefix_check( node->j_prefix, chara ) ){
      /* 要らない prefix は消去しましてよ。*/
      delete_char( node, node->j_kana_start_point,
		   node->cur_pos - node->j_kana_start_point ) ;
      /* やっぱり、存在できませんのね。思った通りですわ。*/
      if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'n' ) &&
	  IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
	/* 「n」を「ん」に変換しないといけないのかどうかをチェック。*/
	/* 仮名開始位置に表示されている「n」を消す。*/
	j_n_convert_to_kana( node ) ;
	/* 仮名入力開始位置をずらす。*/
	node->j_kana_start_point = node->cur_pos ;
	/* 今回入力した文字をカーソル位置に代入する。*/
	j_insert_chara( node, node->cur_pos, chara ) ;
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
	/* j_kana_mode = True */
	break ;
      }
      if( is_okurigana_start_ok( node ) ){
	/* カーソル位置が送り仮名の開始ポイント。*/
	node->j_okurigana_start_point = node->cur_pos ;
	/* 送り仮名の開始ポイントに「*」を挿入する。*/
	j_insert_chara( node, node->cur_pos, '*' ) ;
	/* 送り仮名モードへ移行する。*/
	node->j_okurigana_mode = True ;
	MYCHAR_SET_CHARA( node->j_okuri_chara, chara ) ;
      }
      node->j_kana_start_point = node->cur_pos ;
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
    } else {
      if( !IS_END_OF_STRING( node->j_prefix[ 0 ] ) && 
	  is_okurigana_start_ok( node ) ){
	/* まず入力中の文字列を消去する。*/
	j_delete_region
	  ( node, node->j_kana_start_point, node->cur_pos ) ;
	/* カーソル位置が送り仮名の開始ポイント。*/
	node->j_okurigana_start_point = node->cur_pos ;
	/* 送り仮名の開始ポイントに「*」を挿入する。*/
	j_insert_chara( node, node->cur_pos, '*' ) ;
	/* 送り仮名モードへ移行する。*/
	node->j_okurigana_mode = True ;
	/* なんと、今回入れたので引くみたい…すごい。*/
	MYCHAR_SET_CHARA( node->j_okuri_chara, chara ) ;
	node->j_kana_start_point = node->cur_pos ;
      }
    }
    /* 仮名入力モードへ移行。*/
    node->j_kana_mode = True ;
    /* 今回入力した文字を表示する。*/
    j_insert_chara( node, node->cur_pos, chara ) ;
    j_prefix_add( node->j_prefix, chara ) ;
    break ;

    /* 変換モードで大文字アルファベットが入力された場合には送り仮名モードへ */
  case J_HENKAN_MODE_ON  | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_OFF :
    j_set_henkan_point_label2 : ;
    /* ここでカーソル位置が 0 より大きいとしていますけど、これは正し *
     * い。理由は簡単。一番先頭で変換を開始しても、2 からになるから。*/
    if( is_okurigana_start_ok( node ) ){
      /* カーソル位置が送り仮名の開始ポイント。*/
      node->j_okurigana_start_point = node->cur_pos ;
      /* 送り仮名の開始ポイントに「*」を挿入する。*/
      j_insert_chara( node, node->cur_pos, '*' ) ;
      /* 送り仮名モードへ移行する。*/
      node->j_okurigana_mode = True ;
      MYCHAR_SET_CHARA( node->j_okuri_chara, chara ) ;
    }
    /* カーソル位置の設定。*/
    node->j_kana_start_point = node->cur_pos ;
    /* 仮名入力モードへ移行。*/
    node->j_kana_mode = True ;
    /* 今回入力した文字を表示する。*/
    j_insert_chara( node, node->cur_pos, chara ) ;
    MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
    break ;

    /* 「▽た*っs」というような状態である。変換モードであり、送り仮名モード *
     * であり、仮名モードであるというのは。しかし、仮名モードでないならば   *
     * 送り仮名モードが入る筈はないので、この状態は存在しない。             */
  case J_HENKAN_MODE_ON  | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_ON  :
    break ;

    /* 「▽た*っs」というような状態である。変換モードであり、送り仮名モード *
     * であり、仮名モードであるというのは。                                 */
  case J_HENKAN_MODE_ON  | J_HENKAN_OFF | J_KANA_MODE_ON  | J_OKURIGANA_ON  :
    if( rom_kana_rule_list_check( node, rchara ) ){
      break ;
    }
    /* 仮に以前の入力が "o" という状況だったら…という状況はありえない。*
     * 理由は j_henkan_off ではないから。必ず、変換に入っている！ */
#if defined(DEBUG)
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'o' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
      fprintf
	( stderr, "stray \"o\" in j-set-henkan-point-by-consonat.\n" ) ;
    }
#endif
    /* この場合は大文字入力も小文字入力も関係がない。小文字の時の処理をやる。*/
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'n' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
      /* 「n」を「ん」に変換しないといけないのかどうかをチェック。*/
      /* 仮名開始位置に表示されている「n」を消す。*/
      j_n_convert_to_kana( node ) ;
      /* 仮名入力開始位置をずらす。*/
      node->j_kana_start_point = node->cur_pos ;
      /* 今回入力した文字をカーソル位置に代入する。*/
      j_insert_chara( node, node->cur_pos, chara ) ;
      MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
      /* j_kana_mode = True */
      /* えっと、「ん」に確定してしまうということは変換作業が必要になる。*/
      /* と思ったけど、そうでもないみたい。*/
      break ;
    }
    /* 促音かどうかをチェックするのである。*/
    if( IS_ASCII_EQUAL( node->textbuffer[ node->j_kana_start_point ],
			chara ) && chara != 'x' ){
      /* そこに表示されている文字を消しましょう。*/
      delete_char( node, node->j_kana_start_point, 1 ) ;
      /* そこに促音便を代入しましょう。*/
      /* このとき、cur_pos == j_kana_start_point の筈である。*/
      if( node->j_katakana_mode ){
	MYCHAR_SET_JISX0208_1983( lchara, 0x2543 /*"ッ"*/ ) ;
      } else {
	MYCHAR_SET_JISX0208_1983( lchara, 0x2443 /*"っ"*/ ) ;
      }
      j_insert_myChara( node, node->j_kana_start_point, lchara ) ;
      /* 仮名の入力位置をずらしました、と。*/
      node->j_kana_start_point = node->cur_pos ;
      /* 今回入力した文字をカーソル位置に代入する。*/
      j_insert_chara( node, node->cur_pos, chara ) ;
      MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
      /* j_kana_mode = True */
      break ;
    }
    /* 果たして、そんな prefix は存在できますの？ もりやさん。*/
    if( !j_kana_prefix_check( node->j_prefix, chara ) ){
      /* やっぱり、存在できませんのね。思った通りですわ。*/
      /* 要らない prefix は消去しましてよ。*/
      delete_char( node, node->j_kana_start_point,
		   node->cur_pos - node->j_kana_start_point ) ;
      /* 仮名の変換開始位置も変更しますからね。*/
      node->j_kana_start_point = node->cur_pos ;
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
    }
    /* 今回入力した文字の表示。*/
    j_insert_chara( node, node->cur_pos, chara ) ;
    j_prefix_add( node->j_prefix, chara ) ;
    break ;
    
  case J_HENKAN_MODE_ON  | J_HENKAN_ON  | J_KANA_MODE_OFF | J_OKURIGANA_OFF :
    /* この場合には確定作業が行われた後、not_active, henkan_off と同じ処理。*/
    j_kakutei( gw, node ) ;
    goto j_set_henkan_point_label1 ;

    /* 変換が行われている場合で、仮名prefix入力なし、送り仮名入力のある場合。*/
  case J_HENKAN_MODE_ON  | J_HENKAN_ON  | J_KANA_MODE_OFF | J_OKURIGANA_ON  :
    /* この場合には確定作業が行われた後、not_active, henkan_off, okuri_off */
    j_kakutei( gw, node ) ;
    goto j_set_henkan_point_label1 ;

  case J_HENKAN_MODE_ON  | J_HENKAN_ON  | J_KANA_MODE_ON  | J_OKURIGANA_OFF :
  case J_HENKAN_MODE_ON  | J_HENKAN_ON  | J_KANA_MODE_ON  | J_OKURIGANA_ON  :
    /* o によって、仮名モードであり、かつ変換中かつ送り仮名モードが存
       在する。その時にどうするか？ */
    /* それ以外の場合には、仮名が確定すれば、変換は確定してしまうので、
       この状態になることは無いと考えている。*/
    if( rom_kana_rule_list_check( node, rchara ) ){
      break ;
    }
    /* 確定する。*/
    j_kakutei( gw, node ) ;
    goto j_set_henkan_point_label1 ;
  }
  return 0 ;
}

/*
 * かな漢字変換テーブルを引いて、結果を返す関数。
 */
static struct myChar *my_assoc_table_search
( struct SKKInputNode *node, struct roma_kana_table *table,
  struct myChar *key )
{
  struct roma_kana_table *ptr = table ;
  /* テーブルをひく。*/
  while( !IS_END_OF_STRING( *( ptr->prefix ) )
	 || !IS_END_OF_STRING( *( ptr->kana ) )
	 || !IS_END_OF_STRING( *( ptr->katakana ) ) ){
    if( !myCharStrcmp( ptr->prefix, key ) ){
      if( node->j_katakana_mode ){
	/* 片仮名だった場合の処理。*/
	return ptr->katakana ;
      } else {
	/* 平仮名だった場合の処理。*/
	return ptr->kana ;
      }
    }
    ptr ++ ;
  }
  return NULL ;
}

/*
 * 送り仮名を設定し、変換作業へと移動する関数。
 *-----
 * つまり、「▽み」の状態で大文字の "R" を入力した時に、「▽み*る」に
 * なって 「▼見る」へと変化させる作業を行う。
 */
int j_set_okurigana
( Widget gw, struct skkinputBuffer *sbuffer, 
  struct SKKInputNode *node, struct myChar j_okuri_chara )
{
  int usage, copy_sareta_mojisuu ;
  int henkan_stpos, okuri_stpos, okuri_endpos, copy_suru_mojisuu ;
  /* 変換終了位置の設定。*/
  node->j_henkan_end_point = node->j_okurigana_start_point ;
  /* '*' が存在するかどうかのチェック。*/
  if( !IS_ASCII_EQUAL
      ( node->textbuffer[ node->j_okurigana_start_point ], '*' ) ){
    j_insert_chara( node, node->j_okurigana_start_point, '*' ) ;
  }
  /* 単語登録の時に表示される文字列を保存しておく。*/
  copy_suru_mojisuu =
    node->j_okurigana_start_point - node->j_henkan_start_point ;
  /* バッファをあふれることなんて無いと思うけどね。*/
  if( copy_suru_mojisuu >= 0 ){
    henkan_stpos = node->j_henkan_start_point ;
  } else {
    henkan_stpos      = node->j_okurigana_start_point ;
    copy_suru_mojisuu = -copy_suru_mojisuu ;
  }
  if( node->j_katakana_mode ){
    copy_sareta_mojisuu = myCharHiraKataStrnncpy
      ( node->j_henkan_key2,             TEXTMAXLEN,
	node->textbuffer + henkan_stpos, copy_suru_mojisuu ) ;
  } else {
    myCharStrncpy
      ( node->j_henkan_key2, node->textbuffer + henkan_stpos,
	copy_suru_mojisuu ) ;
    copy_sareta_mojisuu = copy_suru_mojisuu ;
  }
  usage = copy_sareta_mojisuu ;
  MYCHAR_SET_CHARA( node->j_henkan_key2[ usage ], '*' ) ;
  usage ++ ;
  MYCHAR_SET_END_OF_STRING( node->j_henkan_key2[ usage ] ) ;
  /* 次に送り仮名の方を抜き出して加える。*/
  okuri_stpos = ( node->j_okurigana_start_point < TEXTMAXLEN )?
    node->j_okurigana_start_point + 1 : node->j_okurigana_start_point ;
  if( node->j_kana_mode ){
    okuri_endpos = node->j_kana_start_point ;
  } else {
    okuri_endpos = node->cur_pos ;
  }
  /* どの位の文字を抜き出したの？ */
  if( okuri_stpos > okuri_endpos ){
    swap_int( okuri_stpos, okuri_endpos ) ;
  }
  copy_suru_mojisuu = okuri_endpos - okuri_stpos ;
  /* ええと、バッファを溢れさせることが無いように設定します。*/
  if( ( copy_suru_mojisuu + usage ) > TEXTMAXLEN ){
    copy_suru_mojisuu = TEXTMAXLEN - usage ;
  }
  /* ええと、送り文字列を加えます。*/
  if( node->j_katakana_mode ){
    copy_sareta_mojisuu = myCharHiraKataStrnncat
      ( node->j_henkan_key2,            copy_suru_mojisuu,
	node->textbuffer + okuri_stpos, copy_suru_mojisuu ) ;
  } else {
    myCharStrncat
      ( node->j_henkan_key2, node->textbuffer + okuri_stpos,
	copy_suru_mojisuu ) ;
    copy_sareta_mojisuu = copy_suru_mojisuu ;
  }
  MYCHAR_SET_END_OF_STRING
    ( node->j_henkan_key2[ copy_sareta_mojisuu + usage ] ) ;

  copy_suru_mojisuu = node->j_henkan_end_point - node->j_henkan_start_point ;
  if( copy_suru_mojisuu >= 0 ){
    henkan_stpos      = node->j_henkan_start_point ;
  } else {
    henkan_stpos      = node->j_henkan_end_point ;
    copy_suru_mojisuu = -copy_suru_mojisuu ;
  }
  /* 変換キーの設定。*/
  if( node->j_katakana_mode ){
    copy_sareta_mojisuu = myCharHiraKataStrnncpy
      ( node->j_henkan_key,              TEXTMAXLEN,
	node->textbuffer + henkan_stpos, copy_suru_mojisuu ) ;
  } else {
    myCharStrncpy
      ( node->j_henkan_key, node->textbuffer + henkan_stpos,
	copy_suru_mojisuu ) ;
    copy_sareta_mojisuu = copy_suru_mojisuu ;
  }
  node->j_okurigana_offset       = usage = copy_sareta_mojisuu ;
  node->j_henkan_key[ usage ++ ] = j_okuri_chara ;
  MYCHAR_SET_END_OF_STRING( node->j_henkan_key[ usage ] ) ;
  /* `*' を消去する。*/
  j_delete_region
    ( node, 
      node->j_okurigana_start_point, node->j_okurigana_start_point + 1 ) ;
  /* 変換作業を行う。*/
  j_henkan( gw, sbuffer, node ) ;
  return 0 ;
}

/*
 * かなが確定する関数。
 *----
 * kana inserting functions.
 */
static int j_kanakakutei
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node,
  struct roma_kana_table *table, int chara )
{
  struct myChar j_okuri_chara, *str ;

  /* 仮名入力の途中ですか？ */
  if( !node->j_kana_mode ){
    /* 仮名入力の途中でない場合の処理。*/
#ifdef DEBUG_LV0
    fprintf( stderr, "Now not kana input mode\n" ) ;
#endif
    /* 仮名入力の開始位置を修正する。*/
    node->j_kana_start_point = node->cur_pos ;
    /* 仮名 prefix は仮名の確定によって空になる。*/
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
    j_okuri_chara = node->j_okuri_chara ;
  } else {
    /* 仮名入力の途中の場合の処理…でも、rule-list もあり。*/
    if( rom_kana_rule_list_check( node, chara ) )
      goto j_kanakakutei_rom_kana_rulelist_hit ;

    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ],'o' ) ){
      if( IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
	node->j_kana_mode = False ;
      } else if( IS_ASCII_EQUAL( node->j_prefix[ 1 ],'h' ) &&
		 IS_END_OF_STRING( node->j_prefix[ 2 ] ) ){
	MYCHAR_SET_CHARA( node->j_prefix[ 0 ], 'h' ) ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
      }
    }
    j_okuri_chara = node->j_okuri_chara ;
  }
  /* 仮名の prefix を見て、ローマ字仮名変換を行う。*/
  str = my_assoc_table_search( node, table, node->j_prefix ) ;
  /* ローマ字仮名変換に成功しましたか？ */
  if( str == NULL ){
    /* ローマ字仮名変換に失敗した場合の処理。*/
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
    /* j_okuri_chara = '\0' ; */
    str = my_assoc_table_search( node, table, node->j_prefix ) ;
  }
  /* 確定してしまう。*/
  /* if( j_kakutei_early && j_henkan_mode ) */
  if( node->j_henkan_on ){
    /* 仮名入力の途中だったら駄目。*/
    j_kakutei( gw, node ) ;
  }
  if( node->j_kana_mode ){
    /* kana prefix の長さ分だけ文字を消す。*/
    j_delete_region( node, node->j_kana_start_point, node->cur_pos ) ;
    node->j_kana_mode = False ;
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
  }
  /* 消した場所に仮名を入力する。*/
  j_insert_str( node, node->cur_pos, str ) ;
  /* 送り仮名モードだった場合には、送り仮名を設定する。*/
  /* つまり、送り仮名が確定して変換に移れるのだよね。*/
  if( node->j_okurigana_mode )
    j_set_okurigana( gw, sbuffer, node, j_okuri_chara ) ;
  return 0 ;

j_kanakakutei_rom_kana_rulelist_hit:
  if( node->j_henkan_on )
    j_kakutei( gw, node ) ;
  if( node->j_okurigana_mode )
    j_set_okurigana( gw, sbuffer, node, node->j_okuri_chara ) ;
  return 0 ;
}

/* 母音が入力された場合には、仮名入力の確定とかがある。*/
int j_insert_a
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  return j_kanakakutei( gw, sbuffer, node, skk_roma_kana_a, chara ) ;
}

/* 母音が入力された場合には、仮名入力の確定とかがある。*/
int j_insert_i
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  return j_kanakakutei( gw, sbuffer, node, skk_roma_kana_i, chara ) ;
}

/* 母音が入力された場合には、仮名入力の確定とかがある。*/
int j_insert_u
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  return j_kanakakutei( gw, sbuffer, node, skk_roma_kana_u, chara ) ;
}

/* 母音が入力された場合には、仮名入力の確定とかがある。*/
int j_insert_e
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  return j_kanakakutei( gw, sbuffer, node, skk_roma_kana_e, chara ) ;
}

/* 母音が入力された場合には、仮名入力の確定とかがある。*/
int j_insert_o
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  return j_kanakakutei( gw, sbuffer, node, skk_roma_kana_o, chara ) ;
}

/*
 * 大文字のアルファベットが入力されたことにより、変換開始位置が決定され
 * 変換モードへと移行するという処理を行う関数。
 * -----
 * 母音が入力された場合の処理を行う。
 * -----
 * 母音の入力 ....
 *   1. 仮名入力がされている ... ローマ字仮名の変換を行う。
 *   2. ▽の表示と仮名入力,変換開始点を設定する。
 *   3. 送り仮名モードは必要か？
 */
static int j_set_henkan_point_ByVowel
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node,
  struct roma_kana_table *table, int chara )
{
  /* skkinput の内部状態を抜き出す。*/
  int status = ( node->j_henkan_mode << 3 ) | ( node->j_henkan_on << 2 ) |
    ( node->j_kana_mode << 1 ) | ( node->j_okurigana_mode ) ;
  int pos, rchara ;
  struct myChar pchara, lchara ;

#ifdef DEBUG
  fprintf( stderr, "V( j_henkan_mode, j_henkan_on, j_kana_mode,\
j_okurigana_mode ) = (%d,%d,%d,%d)\n", node->j_henkan_mode,
	   node->j_henkan_on, node->j_kana_mode,
	   node->j_okurigana_mode ) ;
#endif

  /* 大文字、小文字の変換。*/
  rchara = chara ;
  if( chara >= 'A' && chara <='Z' )
    chara += 'a' - 'A' ;

  /* 状態遷移表。*/
  switch( status ){

    /* 何も入力されていない状態からの変換位置指定。*/
    /* この場合は母音からいきなり仮名に変換して良いので、仮名モードはオフ *
     * のままなのである。                                                 */
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_OFF :
    j_set_henkan_point_label1 : ;
    /* ▽を表示して。*/
    MYCHAR_SET_JISX0208_1983( lchara, 0x2226 /*"▽"*/ ) ;
    j_insert_myChara( node, node->cur_pos, lchara ) ;
    /* 変換開始位置をカーソル位置に決定して。*/
    node->j_henkan_start_point = node->cur_pos ;
    /* 変換モードをオンにする。*/
    node->j_henkan_mode = True ;
    /* ローマ字仮名変換を行う。*/
    j_kanakakutei( gw, sbuffer, node, table, chara ) ;
    /* "oh" の処理のため。仮名モードがオフでないと、この処理はない。そ
       うでなければ、母音としての o によって仮名が確定されてしまうから 
       である。*/
    if( chara == 'o' ){
      MYCHAR_SET_CHARA( node->j_prefix[ 0 ], 'o' ) ;
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
      /* 仮名モードがオンになる！ */
      node->j_kana_mode   = True ;
      /* "o" を入力した次の場所が開始位置。*/
      node->j_kana_start_point = node->cur_pos ;
    } else {
      /* 当然ながら、仮名モードはオフ。*/
      node->j_kana_mode   = False ;
    }
    break ;

  case J_HENKAN_MODE_ON  | J_HENKAN_OFF | J_KANA_MODE_ON  | J_OKURIGANA_OFF :
    pchara = last_character_of( node->j_prefix ) ;
    /* ルールリストをひく。*/
    if( rom_kana_rule_list_check( node, rchara ) ){
      break ;
    }
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'o' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
      node->j_kana_mode = False ;
      /* この仮名モードは "o" によるものであったから、もともと存在し *
       * ないものとして扱う。*/
      goto j_set_henkan_point_label2 ;
    }
    /* 送り仮名変換にして大丈夫なのかな？ */
    if( is_okurigana_start_ok( node ) ){
      node->cur_pos_backup = node->cur_pos ;
      /* カーソル位置が送り仮名の開始ポイント。*/
      node->cur_pos = node->j_okurigana_start_point =
	node->j_kana_start_point ;
      /* 送り仮名の開始ポイントに「*」を挿入する。*/
      j_insert_chara( node, node->j_okurigana_start_point, '*' ) ;
      /* 仮名開始位置の修正。*/
      node->j_kana_start_point = node->cur_pos ;
      /* カーソル位置を元に戻す。*/
      node->cur_pos = node->cur_pos_backup ;
      /* 送り仮名モードへ移行する。*/
      node->j_okurigana_mode = True ;
      node->j_okuri_chara = pchara ;
    }
    /* ローマ字仮名変換を行う。*/
    j_kanakakutei( gw, sbuffer, node, table, chara ) ;
    /* 当然ながら、仮名モードはオフ。*/
#if 0
    node->j_kana_mode   = False ;
    node->j_prefix[ 0 ] = '\0' ;
#endif
    break ;
    
    /* 既に仮名モードがオンであった場合の処理。*/
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_ON  | J_OKURIGANA_OFF :
    /* ルールリストをひく。*/
    if( rom_kana_rule_list_check( node, rchara ) ){
      break ;
    }
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'o' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
      node->j_kana_mode = False ;
      /* この仮名モードは "o" によるものであったから、もともと存在し *
       * ないものとして扱う。*/
      goto j_set_henkan_point_label1 ;
    }
    /* 以前の仮名開始位置を記憶しておく。*/
    pos = node->j_kana_start_point ;
#if 0
    /* if( IS_ASCII_EQUAL( node->textbuffer[ pos ], 'n' ) ){*/
#else
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'n' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
#endif
      j_n_convert_to_kana( node ) ;
      node->j_kana_mode = False ;
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
      pos = node->cur_pos ;
    }
    /* ローマ字仮名変換を行う。*/
    j_kanakakutei( gw, sbuffer, node, table, chara ) ;
    /* ▽を表示して。*/
    MYCHAR_SET_JISX0208_1983( lchara, 0x2226 /*"▽"*/ ) ;
    j_insert_myChara( node, pos, lchara ) ;
    /* 変換モードをオンにする。*/
    node->j_henkan_mode = True ;
    /* 変換開始位置を決定して。*/
    node->j_henkan_start_point = pos + 1 ;
#if 0
    /* 当然ながら、仮名モードはオフ。*/
    node->j_kana_mode   = False ;
#endif
    break ;

    /* 変換中でないのに送り仮名モードになることはない。*/
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_ON :
  case J_HENKAN_MODE_OFF | J_HENKAN_OFF | J_KANA_MODE_ON  | J_OKURIGANA_ON :
    break ;

    /* 変換モードに移行していないのに、変換中であることはない。*/
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_OFF | J_OKURIGANA_OFF :
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_OFF | J_OKURIGANA_ON  :
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_ON  | J_OKURIGANA_OFF :
  case J_HENKAN_MODE_OFF | J_HENKAN_ON  | J_KANA_MODE_ON  | J_OKURIGANA_ON  :
    break ;
    
    /* 変換モードで大文字アルファベットが入力された場合には送り仮名モードへ */
  case J_HENKAN_MODE_ON  | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_OFF :
    j_set_henkan_point_label2 : ;
    if( is_okurigana_start_ok( node ) ){
      /* カーソル位置が送り仮名の開始ポイント。*/
      node->j_okurigana_start_point = node->cur_pos ;
      /* 送り仮名の開始ポイントに「*」を挿入する。*/
      j_insert_chara( node, node->cur_pos, '*' ) ;
      /* 送り仮名モードへ移行する。*/
      node->j_okurigana_mode = True ;
      MYCHAR_SET_CHARA( node->j_okuri_chara, chara ) ;
    }
    /* 今回入力した母音についてローマ字仮名変換を行う。*/
    j_kanakakutei( gw, sbuffer, node, table, chara ) ;
    /* "oh" の処理が入る。*/
    if( chara == 'o' ){
      MYCHAR_SET_CHARA( node->j_prefix[ 0 ], chara ) ;
      MYCHAR_SET_END_OF_STRING( node->j_prefix[ 1 ] ) ;
      /* 仮名モードがオンになる！ */
      node->j_kana_mode   = True ;
      /* "o" を入力した次の場所が開始位置。*/
      node->j_kana_start_point = node->cur_pos ;
    }
    break ;

    /* 「▽た*っs」というような状態である。変換モードであり、送り仮名モード *
     * であり、仮名モードであるというのは。しかし、仮名モードでないならば   *
     * 送り仮名モードが入る筈はないので、この状態は存在しない。             */
  case J_HENKAN_MODE_ON  | J_HENKAN_OFF | J_KANA_MODE_OFF | J_OKURIGANA_ON  :
    break ;

    /* 「▽た*っs」というような状態である。変換モードであり、送り仮名モード *
     * であり、仮名モードであるというのは。                                 */
  case J_HENKAN_MODE_ON  | J_HENKAN_OFF | J_KANA_MODE_ON  | J_OKURIGANA_ON  :
    /* ルールリストをひく。*/
    if( rom_kana_rule_list_check( node, rchara ) ){
      break ;
    }
    /* 仮に以前の入力が "o" という状況だったら…という状況はありえない。*
     * 理由は j_henkan_off ではないから。必ず、変換に入っている！ */
#if defined(DEBUG)
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'o'  ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
      fprintf
	( stderr, "stray \"o\" in j-set-henkan-point-by-vowel.\n" ) ;
    }
#endif
    /* この場合は大文字入力も小文字入力も関係がない。小文字の時の処理をやる。*/
    /* ローマ字仮名変換を行う。*/
    j_kanakakutei( gw, sbuffer, node, table, chara ) ;
#if 0
    /* 仮名モードは終了する。*/
    node->j_kana_mode = False ;
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
#endif
    break ;
	
  case J_HENKAN_MODE_ON  | J_HENKAN_ON  | J_KANA_MODE_OFF | J_OKURIGANA_OFF :
    /* この場合には確定作業が行われた後、not_active, henkan_off と同じ処理。*/
    j_kakutei( gw, node ) ;
    goto j_set_henkan_point_label1 ;

    /* 変換が行われている場合で、仮名prefix入力なし、送り仮名入力のある場合。*/
  case J_HENKAN_MODE_ON  | J_HENKAN_ON  | J_KANA_MODE_OFF | J_OKURIGANA_ON  :
    /* この場合には確定作業が行われた後、not_active, henkan_off, okuri_off */
    j_kakutei( gw, node ) ;
    goto j_set_henkan_point_label1 ;

  case J_HENKAN_MODE_ON  | J_HENKAN_ON  | J_KANA_MODE_ON  | J_OKURIGANA_OFF :
  case J_HENKAN_MODE_ON  | J_HENKAN_ON  | J_KANA_MODE_ON  | J_OKURIGANA_ON  :
    j_kakutei( gw, node ) ;
    goto j_set_henkan_point_label1 ;
  }
  return 0 ;
}

/*
 * 特殊な見出し文字によって変換開始位置が決定する場合の処理。
 */
static int j_set_henkan_point_by_special_midashi_char
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  struct myChar lchara ;

  /* 仮名入力中かつ"n"なら"ん"にしてしまう。*/
  if( node->j_kana_mode ){
    /* ルールリストをひく。*/
    if( rom_kana_rule_list_check( node, chara ) ){
      return 0 ;
    }
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'o' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
      /* 仮名入力中ならキャンセルしてしまう。*/
      goto j_set_henkan_point_by_special_midashi_char_label1 ;
    }
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'o' ) &&
	IS_ASCII_EQUAL( node->j_prefix[ 1 ], 'h' ) &&
	IS_END_OF_STRING( node->j_prefix[ 2 ] ) ){
      if( IS_ASCII_EQUAL( node->textbuffer[ node->j_kana_start_point ],
			  'h' ) ){
	delete_char
	  ( node, node->j_kana_start_point, 1 ) ;
	if( node->j_katakana_mode ){
	  MYCHAR_SET_JISX0208_1983( lchara, 0x252a /*"オ"*/ ) ;
	} else {
	  MYCHAR_SET_JISX0208_1983( lchara, 0x242a /*"お"*/ ) ;
	}
	j_insert_myChara( node, node->j_kana_start_point, lchara ) ;
      }
      /* 仮名入力中ならキャンセルしてしまう。*/
      goto j_set_henkan_point_by_special_midashi_char_label1 ;
    }
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'n' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
      j_n_convert_to_kana( node ) ;
    } else {
      delete_char
	( node, node->j_kana_start_point,
	  node->cur_pos - node->j_kana_start_point ) ;
    }
  j_set_henkan_point_by_special_midashi_char_label1:
    /* 仮名入力中ならキャンセルしてしまう。*/
    node->j_kana_mode = False ;
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
  }
  /* さて…変換モードで変換動作中なのかなあ？ */
  if( node->j_henkan_mode == False || node->j_henkan_on == True ){
    if( node->j_henkan_mode ){
      /* 変換開始位置を設定できるそうな。*/
      j_set_henkan_point_subr( gw, sbuffer, node, chara ) ;
      /* 正しいのか？ これは…。*/
      j_insert_chara( node, node->cur_pos, chara ) ;
    } else if( node->j_henkan_on ){
      /* オリジナルのマップをエミュレートするそうな。*/
      j_self_insert( gw, sbuffer, node, chara ) ;
    } else {
      /* ただ文字列が入るそうな。*/
      j_self_insert( gw, sbuffer, node, chara ) ;
    }
  } else {
    /* 変換開始前の処理。*/
    j_start_henkan_sub( gw, node ) ;
    /* 特殊文字を挿入する。*/
    j_insert_chara( node, node->cur_pos, chara ) ;
    /* 変換終了位置を設定する。*/
    node->j_henkan_end_point = node->cur_pos ;
    /* 変換キーを切り出す。*/
    myCharStrncpy
      ( node->j_henkan_key,
	node->textbuffer + node->j_henkan_start_point,
	node->j_henkan_end_point - node->j_henkan_start_point ) ;
    MYCHAR_SET_END_OF_STRING
      ( node->j_henkan_key
	[ node->j_henkan_end_point - node->j_henkan_start_point ] ) ;
    /*
     *「もしかして、送り仮名モードということはございませんでしょうか？」
     */
    if( node->j_okurigana_mode ){
      /* 不幸にしてずれないことを祈っているのだが…。*/
      if( node->j_okurigana_start_point > node->j_henkan_start_point ){
	node->j_okurigana_offset =
	  node->j_okurigana_start_point - node->j_henkan_start_point ;
      } else {
	node->j_okurigana_offset =
	  node->j_henkan_end_point - node->j_henkan_start_point ;
      }
    } else {
      node->j_okurigana_offset =
	node->j_henkan_end_point - node->j_henkan_start_point ;
    }
    /* 単語登録の時に使うキーを保存しておく。*/
    myCharStrcpy( node->j_henkan_key2, node->j_henkan_key ) ;
    myCharStrcpy( node->j_search_key, node->j_henkan_key ) ;
    /* さて、変換を行います。*/
    j_henkan( gw, sbuffer, node ) ;
  }
  return 0 ;
}


/*
 * カーソル位置に文字を挿入する関数。
 * ----
 * ただし、skk流に文字を挿入するのであり、それが j_insert と異なっている。
 * j_insert は emacs-lisp の insert を念頭に置いて作った関数なのである。
 */
int j_self_insert
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  if( chara < 0 || chara > 128 || skkinput_input_vector == NULL )
    return 1 ;

  /* 変換動作中なら確定する。*/
  if( node->j_henkan_on == True )
    j_kakutei( gw, node ) ;
  /* 新たな入力により仮名prefixの確定ということが生じる。*/
  if( j_kakutei_kana_mode( gw, sbuffer, node, chara ) )
    return 0 ;

  /* 上変換テーブル内に記述があるならば、それを利用する。*/
  if( skkinput_input_vector[ chara ] != NULL ){
    j_insert_str
      ( node, node->cur_pos, skkinput_input_vector[ chara ] ) ;
  } else {
    /* それ以外は default の keymap に従うとのことである。*/
    j_insert_chara( node, node->cur_pos, chara ) ;
  }
  return 0 ;
}

int j_insert_zenkaku
( struct SKKInputNode *node, int pos, int chara )
{
  if( chara < 0 || chara > 128 || skkinput_zenkaku_vector == NULL )
    return 1 ;
  /* 上変換テーブル内に記述があるならば、それを利用する。*/
  if( skkinput_zenkaku_vector[ chara ] != NULL ){
    j_insert_str
      ( node, node->cur_pos, skkinput_zenkaku_vector[ chara ] ) ;
  }
  return 0 ;
}

static int j_char_to_hex( struct myChar chara )
{
  if( !IS_ASCII_CHARA( chara ) )
    return 0 ;
  if( chara.chara >='a' && chara.chara <='f' )
    return ( chara.chara - 'a' + 10 ) ;
  if( chara.chara >= 'A' && chara.chara <= 'F' )
    return ( chara.chara - 'A' + 10 ) ;
  if( chara.chara >='0' && chara.chara <= '9' )
    return ( chara.chara - '0' ) ;
  return 0 ;
}

/*
 * 今迄の入力を client に送りつける作業を行う。( = 改行？ )
 */
int j_newline
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  struct SKKInputNode *pNode = node->parentbuffer ;
  int len, lchara[ 3 ], i, retkeysend = False ;
  struct myChar chara, *ptr, *tmp ;
  struct myChar j_toroku_string[ TEXTBUFSIZE ] ;
  struct myChar *henkankey ;

  /* 変換の途中もしくは仮名入力の途中であるならば、確定する。ただ  *
   * し、アスキー一バイト系コードの入力モードなら、確定動作はない。*/
  if( node->j_mode ){
    int flag = node->j_henkan_mode ;
    j_kakutei( gw, node ) ;
    /* 途中に "n" なんてのが入っていたら、"ん"に確定しない駄目。   *
     * でも、"nn" とか rom-kana-rule-list をひく必要はない。多分。 *
     * よって、引数は "\0" になっている。*/
    j_kakutei_kana_mode( gw, sbuffer, node, '\0' ) ;
    /* 確定のみの j_newline の存在を許すのなら、変換モードにはいって *
     * いたら、兎に角まず確定する…って、これって親だけでやって欲し  *
     * い？ */
    if( flag && sbuffer->egg_like_newline )
      return 0 ;
  }
  /* この newline は一番親のノードで実行されたのでせうか？ */
  if( pNode == NULL ){
    /* Window が Active であった場合。*/

    /* 送信する文字列を記憶しておく。不要な改行が付加されるのを阻止す */
    /* るためにも chatadapter mode の判定前に行う。*/
    if( sbuffer->historybuffer != NULL &&
	!sbuffer->overthespot_like_input ){
      save_ringbuffer
	( sbuffer->historybuffer, TEXTBUFSIZE, 
	  &sbuffer->hist_start, &sbuffer->hist_end, node->textbuffer ) ;
      sbuffer->hist_cur = -1 ;
    }
    if( !IS_END_OF_STRING( node->textbuffer[ 0 ] ) ){
      /* 文字列を送信する。ここが出来ないので、修正。callback を使う。*/
      XtCallCallbacks( gw, XtNfixNotify, ( XtPointer )node->textbuffer ) ;
      /* テキストバッファを初期化する。*/
      MYCHAR_SET_END_OF_STRING( node->textbuffer[ 0 ] ) ;

      /* over-the-spot モードの場合には、リターンが押されたという情報
       * も必要になる。egg-nl なら関係ないけれどね。*/
      if( sbuffer->chat_adapter || sbuffer->overthespot_like_input )
	retkeysend = True ;
    } else {
      retkeysend = True ;
    }
    /* 改行キーを送信する。*/
    if( retkeysend ){
      j_sendback_keypress( gw, &( sbuffer->xevent ) ) ;
    }
    /* カーソルを先頭へと移動させる。*/
    node->cur_pos        = 0 ;
    node->cur_pos2       = 0 ;
    node->cur_pos3       = 0 ;
    node->cur_pos_backup = 0 ;
    node->cur_pos_top    = 0 ;
    node->j_okurigana_start_point = -1 ;
    node->j_kana_start_point      = -1 ;
    node->j_henkan_start_point    = -1 ;
    node->j_henkan_end_point      = -1 ;
    return 0 ;
  }

  /* この newline は何処ぞのミニバッファで実行されたのでした。(合掌) */

  switch( node->j_minibuff_usage ){
    /* ミニバッファでの変換に移っていた場合…。*/
  case J_HENKAN_IN_MINIBUFF_MODE :
    /* ミニバッファ内で何か文字列が入力されていたのなら、それを親へと *
     * 返さなければならない。もし何も入力されていないのなら、以前の入 * 
     * 力を残していないといけない。*/
    if( !IS_END_OF_STRING( node->textbuffer[ node->cur_pos_top ] ) ){
      j_delete_region
	( pNode, pNode->j_henkan_start_point, pNode->j_henkan_end_point ) ;

      /* ここで出てくるセミコロンは註釈ではないので、変換する必要あり。 */
      myCharStrcpy
	( j_toroku_string, node->textbuffer + node->cur_pos_top ) ;
      for( tmp = j_toroku_string ; !IS_END_OF_STRING( *tmp ) ; tmp ++ ){
	if( IS_ASCII_CHARA( *tmp ) && IS_ASCII_EQUAL(*tmp, ';')){
	  j_quote_semicolon ( j_toroku_string ) ;
	  break ;
	}
      }

      /* mini-buffer 内の「▽あいじょう 」以下の文字列を拾って来て、親 *
       * へと返す。*/
#if 0
      myCharStrcpy ( temporary_buffer, j_toroku_string ) ;
      j_insert_word
	( pNode, pNode->j_henkan_start_point, temporary_buffer ) ;
#else
      j_insert_word
	( pNode, pNode->j_henkan_start_point, j_toroku_string ) ;
#endif
      pNode->j_henkan_end_point = pNode->cur_pos ;

      /* ここで、辞書の更新がなされるが...うまく動くかな？ */
      henkankey = ( pNode->j_okurigana_mode )? 
	pNode->j_henkan_key : pNode->j_search_key ;
      /* そのまま登録されてしまうとまずい人達がいるかもしれないので修 *
       * 正する。*/
      j_quote_char( j_toroku_string ) ;
      
      /* 註釈でないセミコロンも修正する。 */
      for( tmp = j_toroku_string ; !IS_END_OF_STRING( *tmp ) ; tmp ++ ){
	if( IS_ASCII_CHARA( *tmp ) && IS_ASCII_EQUAL(*tmp, ';')){
	  j_quote_semicolon ( j_toroku_string ) ;
	  break ;
	}
      }

      add_SudeniHenkanKakuteiShitamonoByString
	( henkankey, j_toroku_string, node->j_okurigana_mode ) ;
      /* 登録した単語の数を一つ増やす。*/
      skkinput_j_count_touroku ++ ;

      pNode->j_current_henkan_vector_index = NULL ;
      /* 親御さんは確定します。*/
      j_kakutei( gw, pNode ) ;
    } else {
      /* ミニバッファでの変換モードに入っていながら、ちっともヒット *
       * するものが無かった場合の処理。*/
      if( pNode->j_henkan_count == 0 && pNode->j_henkan_mode &&
	  pNode->j_henkan_on ){
	/* ▼のままではなく▽へと状態遷移させる。*/
	j_keyboard_quit_henkanrestart
	  ( gw, pNode, pNode->j_okurigana_mode ) ;
      }
    }
    break ;
  case J_INPUT_BY_CODE_OR_MENU_MODE :
    ptr = node->textbuffer + node->cur_pos_top ;
    while( IS_ASCII_EQUAL( *ptr, 0x20 ) || IS_ASCII_EQUAL( *ptr, 0x08 ) )
      ptr ++ ;
    len = myCharStrlen( ptr ) >> 1 ;
    if( len == 0 ){
      lchara[ 0 ] = j_code_n1_min ;
      pNode->j_input_by_code_current_charset = CHARSET_JISX0208_1983 ;
      goto j_input_by_code_or_menu_jmp_start ;
    }
    /* 文字数が足りなければ、処理を抜ける。*/
    if( len < 2 ){
      pNode->j_input_by_code_or_menu_mode = False ;
      break ;
    }
    if( len > 3 )
      len = 3 ;
    /* 入力されたのが何番なのかを調べる。*/
    for( i = 0 ; i < len ; i ++ ){
      lchara[ i ] = 
	( j_char_to_hex( ptr[ 0 ] ) << 4 | 
	  j_char_to_hex( ptr[ 1 ] ) ) ;
      ptr ++ ; ptr ++ ;
    }
    /* 入力文字数が無茶苦茶多くても無視してるからね！ */
    switch( lchara[ 0 ] ){
    case 0x8E :
      chara.charset = CHARSET_JISX0201_1976 ;
      chara.chara   = lchara[ 1 ] | 0x80 ;
      break ;
    case 0x8F :
      pNode->j_input_by_code_current_charset = CHARSET_JISX0212_1990 ;
      if( len == 2 ){
	lchara[ 0 ] = j_code_n1_min ;
	goto j_input_by_code_or_menu_jmp_start ;
      }
      if( lchara[ 1 ] == 0 ){
	lchara[ 1 ] = lchara[ 2 ] & 0x7F ;
	goto j_input_by_code_or_menu_jmp_start ;
      }
      chara.charset = CHARSET_JISX0212_1990 ;
      chara.chara   = ( ( lchara[ 1 ] << 8 ) | lchara[ 2 ] ) & 0x7F7F ;
      break ;
    default :
      pNode->j_input_by_code_current_charset = CHARSET_JISX0208_1983 ;
      if( len == 1 ){
	lchara[ 0 ] = j_code_n1_min ;
	goto j_input_by_code_or_menu_jmp_start ;
      }
      if( lchara[ 0 ] == 0 ){
	lchara[ 0 ] = lchara[ 1 ] & 0x7F ;
	goto j_input_by_code_or_menu_jmp_start ;
      }
      chara.charset = CHARSET_JISX0208_1983 ;
      chara.chara   = ( ( lchara[ 0 ] << 8 ) | lchara[ 1 ] ) & 0x7F7F ;
      break ;
    }
    /* その場所に文字を入れてしまう…。*/
    pNode->j_input_by_code_or_menu_mode = False ;
    j_insert_myChara
      ( pNode, pNode->j_input_by_code_or_menu_point, chara ) ;
    break ;

  j_input_by_code_or_menu_jmp_start:
    if( lchara[ 0 ] < j_code_n1_min )
      lchara[ 0 ] = j_code_n1_min ;
    if( lchara[ 0 ] > j_code_n1_max )
      lchara[ 0 ] = j_code_n1_max ;
    pNode->j_input_by_code_or_menu_mode = True ;
    pNode->j_input_by_code_current_code = lchara[ 0 ] ;
    j_input_by_code_or_menu_jmp_showCandidate( pNode, lchara[ 0 ] ) ;
    goto j_newline_exit1 ;
  case J_PURGE_YES_OR_NO_P :
    if( !myCharCharStrcmp
	( node->textbuffer + node->cur_pos_top, "yes" ) ){
      /* 親の現在注目している変換候補を抹消する。*/
      purge_SudeniHenkanKakuteiShitamono( pNode ) ;

      /* 表示されている「▼候補」を消去する。*/
      j_delete_region
	( pNode, pNode->j_henkan_start_point, pNode->j_henkan_end_point ) ;
      pNode->j_henkan_end_point = pNode->cur_pos ;
      pNode->j_current_henkan_vector_index = NULL ;
      /* 親御さんは確定します。*/
      j_kakutei( gw, pNode ) ;
    }
    /* この人は purge が no だった場合には何もしません。*/
    break ;
  default :
    break ;
  }
  MYCHAR_SET_END_OF_STRING( pNode->mtextbuffer[ 0 ] ) ;
j_newline_exit1:
  /* ミニバッファの利用は停止されるので、クリアする。*/
  XtVaSetValues( gw, XtNclearMinibuffer, True, NULL ) ;
  /* このミニバッファは不要になったので、解放しませう。*/
  free_Minibuffer( gw, sbuffer, node ) ;
  /* 親にカーソルを返しましょう。*/
  pNode->cur_exist = True ;
  return 0 ;
}

/*
 * 仮名入力の途中だった場合に、その仮名プレフィクスを削除する処理をする関数。
 */
void j_delete_kanaprefix( struct SKKInputNode *node )
{
  /* 変換中なら確定にしてしまう。*/
  if( node->j_kana_mode ){
    /* 仮名入力の途中で 'n' の処理が残っていた場合。*/
    /* 仮名入力が 'n' であっても、▽えら*んn という状態が存在でき *
     * るので注意？ */
    if( IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'n' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
      j_n_convert_to_kana( node ) ;
    } else {
      delete_char( node, node->j_kana_start_point,
		   node->cur_pos - node->j_kana_start_point ) ;
    }
    /* 仮名入力中ならキャンセルしてしまう。*/
    node->j_kana_mode = False ;
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
  }
  return ;
}

/*
 * 仮名漢字変換を開始する時に呼出されるコールバック関数。
 */
static void j_get_completion_results
( struct SKKInputNode *node )
{
  SkkinpSearchVector *j_purge_vector_top ;
  VectorIndex *j_purge_vector_index_top ;
  int tmp ;

  /* 前回利用されたかもしれないコンプリーションの情報は捨てる。*/
  free_VectorIndex( &node->j_completion_vector_index_top ) ;
  free_SkkinpSearchVector( &node->j_completion_vector_top ) ;

  node->j_completion_vector_top = NULL ;
  /* 内部に記憶されている辞書から検索を行う。*/
  node->j_completion_vector_top =
    j_completion_search_sudeniKakuteiShitaKouho
    ( node->j_completion_vector_top, node->j_henkan_key ) ;
  /* まず、局所辞書から検索を行う。*/
  node->j_completion_vector_top = 
    j_completion_skkinput_local_jisyo
    ( node->j_completion_vector_top, node->j_henkan_key ) ;
  /* そして、どれか一つでも変換に成功した場合…。*/
  if( node->j_completion_vector_top != NULL ){
    /* こっちはパージの検索。*/
    j_purge_vector_index_top = NULL ;
    j_purge_vector_top       = NULL ;
    j_purge_vector_top = j_completion_search_sudeniPurgeSaretamono
      ( j_purge_vector_top, node->j_henkan_key ) ;
    /* 一旦ハッシュに現在パージされている候補を叩き込む。*/
    /* 切り出した後にハッシュがクリアされては困るので、False であ *
     * る。どれか一つでもヒットしなければ、パージリストを検索する *
     * 意味は全く無い。*/
    if( j_purge_vector_top != NULL ){
      j_purge_vector_index_top = makeSearchVectorIndex
	( j_purge_vector_top, &tmp, False ) ;
    }
    /* SKK からの返事を候補毎に切り出す。*/
    node->j_completion_vector_index_top =
      makeSearchVectorIndex( node->j_completion_vector_top, &tmp, True ) ;
    /* purge なんかの攻撃をかわし、きちんと切り出せるものがあった *
     * 時の処理。*/
    if( node->j_completion_vector_index_top != NULL ){
      /* 一番先頭の候補を指すようにする。*/
      node->j_current_completion_vector_index = 
	node->j_completion_vector_index_top ;
      node->j_completion_mode = True ;
    } else {
      /* purge の影響か…何か辞書から送ってきたんだけど、解析して *
       * みたら駄目だった、そんなの使っちゃいけなかった。*/
      free_SkkinpSearchVector( &node->j_completion_vector_top ) ;
      node->j_current_completion_vector_index = NULL ; 
   }
    /* パージのための情報はもしかしてもう不要？ */
    free_VectorIndex( &j_purge_vector_index_top ) ;
    free_SkkinpSearchVector( &j_purge_vector_top ) ;
  }
  return ;
}

/*
 * 人類補完計画を試みる関数ではなくて、文字列の補完を行う関数。
 */
int j_try_completion( Widget gw, struct SKKInputNode *node )
{
  int length, startpos, endpos ;

  /* 仮名の入力中なら確定にしてしまう。*/
  j_delete_kanaprefix( node ) ;

  if( node->j_completion_mode ){
    /* 次の候補を求める。*/
    if( skkinput_dabbrev_like_completion )
      return find_next_completion( node ) ;
    /* そうでなければ、コンプリーションモードを一旦抜けてから、再度挑
       戦する。*/
    j_completion_close( node ) ;
  }
  /* 変換開始動作を行った後であり、実際の変換が一切行われていないの *
   * なら、completion が開始する。*/
  if( node->j_henkan_mode && !node->j_henkan_on ){
    if( node->cur_pos < node->j_henkan_start_point ){
      startpos = node->cur_pos ;
      endpos   = node->j_henkan_start_point ;
    } else {
      startpos = node->j_henkan_start_point ;
      endpos   = node->cur_pos ;
    }
    length = endpos - startpos ;
    /* 補完しようとした文字列が長さ零だったら。*/
    if( length <= 0 ){
      err_msg( node, "Cannot complete an empty string!" ) ;
      return 1 ;
    }
    /* 取り敢えず、カーソル位置と変換開始位置の間の文字列を抜き出す。*/
    myCharStrncpy
      ( node->j_henkan_key,
	node->textbuffer + startpos, length ) ;
    MYCHAR_SET_END_OF_STRING( node->j_henkan_key[ length ] ) ;
    /* completion にかけてみる…このアルゴリズムは結構腐っている。*
     * (^^;; */
    j_get_completion_results( node ) ;
    
    /* 一つもヒットしませんでした。(^^;;; */
    if( !node->j_completion_mode ){
      int clen ;
      /* これでは漢字が落ちてしまう、どうにかすること！ */
      myCharCharStrcpy
	( node->mtextbuffer, "No completions for \"" ) ;
      if( length >= ( TEXTBUFSIZE - 22 ) ){
	clen = TEXTBUFSIZE - 22 ;
      } else {
	clen = length ;
      }
      myCharStrncat
	( node->mtextbuffer, node->j_henkan_key, clen ) ;
      MYCHAR_SET_CHARA
	( node->mtextbuffer[ clen + 20 ], 0x22 ) ;
      MYCHAR_SET_END_OF_STRING
	( node->mtextbuffer[ clen + 21 ] ) ;
      return 1 ;
    }
    /* 取り敢えず変換開始位置からカーソル位置までに表示されている文 *
     * 字列を消去する。*/
    j_delete_region( node, startpos, endpos ) ;
    /* 補完できた文字列を抜き出す。*/
    copyCandidate
      ( temporary_buffer,
	( node->j_current_completion_vector_index )->node,
	( node->j_current_completion_vector_index )->position,
	TEXTMAXLEN ) ;
    j_insert_str( node, startpos, temporary_buffer ) ;
  } else {
    /* タブ幅だけ空白を入れる。*/
    for( length = 0 ; length < skkinput_tab_width ; length ++ )
      j_insert_chara( node, node->cur_pos, ' ' ) ;
  }
  return 0 ;
}

/*
 * カーソル位置のコードを表示する関数。
 *-----
 */
int j_display_code_for_char_at_point( struct SKKInputNode *node )
{
  unsigned char tempstring[ TEXTBUFSIZE ] ;
  struct myChar wchara ;

  j_delete_kanaprefix( node ) ;
  /* 行末でなされた場合。*/
  if( IS_END_OF_STRING( node->textbuffer[ node->cur_pos ] ) ){
    err_msg( node, "Cursor is at the end of the buffer." ) ;
    return 1 ;
  }
  /* こんなことは無いと思うけど、一応はねる。*/
  if( node->cur_pos < node->cur_pos_top ){
    node->cur_pos = node->cur_pos_top ;
    return 0 ;
  }
  wchara = node->textbuffer[ node->cur_pos ] ;
  /* 漢字コードなのかなぁ？ そうなのかなぁ？ ほんとうのところはどう *
   * なのかなぁ？ */
  switch( wchara.charset ){
    /* ASCII 及び JISX0201-ROMAN を表示する。*/
  case CHARSET_ISO8859_1 :
  case CHARSET_ISO8859_2 :
  case CHARSET_ISO8859_3 :
  case CHARSET_ISO8859_4 :
  case CHARSET_ISO8859_5 :
  case CHARSET_ISO8859_6 :
  case CHARSET_ISO8859_7 :
  case CHARSET_ISO8859_8 :
  case CHARSET_ISO8859_9 :
    if( wchara.chara & 0x80 ){
      sprintf
	( tempstring, "\"A\"  ISO8859-%d: %02x (%3d)",
	  wchara.charset - CHARSET_ISO8859_1 + 1 ,
	  wchara.chara, wchara.chara ) ;
    } else {
      sprintf
	( tempstring, "\"A\"  %02x (%3d)",
	  wchara.chara, wchara.chara ) ;
    }
    myCharCharStrcpy( node->mtextbuffer, tempstring ) ;
    /* 直接コードを表示しなければならない部分は自前で入れる。*/
    node->mtextbuffer[ 1 ] = wchara ;
    break ;
    /* JISX0201-1976 を表示する。*/
  case CHARSET_JISX0201_1976 :
    if( wchara.chara & 0x80 ){
      sprintf
	( tempstring,
	  "[@]  EUC: 8e%02x (142, %3d), JISX0201: %02x (%3d)", 
	  node->textbuffer[ node->cur_pos ].chara | 0x0080,
	  node->textbuffer[ node->cur_pos ].chara | 0x0080,
	  node->textbuffer[ node->cur_pos ].chara,
	  node->textbuffer[ node->cur_pos ].chara ) ;
      goto multibyte_chara_print ;
    }  else {
      sprintf
	( tempstring, "\"A\"  %02x (%3d)",
	  wchara.chara, wchara.chara ) ;
      myCharCharStrcpy( node->mtextbuffer, tempstring ) ;
      /* 直接コードを表示しなければならない部分は自前で入れる。*/
      node->mtextbuffer[ 1 ] = wchara ;
      break ;
    }
    /* JISX0212-1978, 1983 のコード表示。共通化してしまってる。旧JIS 
     * とか何とか区別する必要はあるの？ */
  case CHARSET_JISX0208_1978 :
  case CHARSET_JISX0208_1983 :
    sprintf
      ( tempstring,
	"[@]  EUC: %04x (%3d, %3d), JISX0208: %04x (%3d, %3d)",
	( wchara.chara | 0x8080 ), 
	( wchara.chara >> 8 ) | 0x80, ( wchara.chara & 0x00FF ) | 0x80,
	wchara.chara,
	( wchara.chara >> 8 ), ( wchara.chara & 0x00FF ) ) ;
    goto multibyte_chara_print ;
    /* JISX0212-1990 のコード表示。*/
  case CHARSET_JISX0212_1990 :
    sprintf
      ( tempstring,
	"[@]  EUC: 8f%04x (143, %3d, %3d), JISX0212: %04x (%3d, %3d)",
	( wchara.chara | 0x8080 ),
	( ( wchara.chara >> 8 ) | 0x80 ), ( ( wchara.chara & 0x00FF ) | 0x80 ),
	wchara.chara,
	( wchara.chara >> 8 ), ( wchara.chara & 0x00FF ) ) ;
    /* 2バイト系の文字の表示の共通部分。*/
  multibyte_chara_print:
    myCharCharStrcpy( node->mtextbuffer, tempstring ) ;
    MYCHAR_SET_JISX0208_1983
      ( node->mtextbuffer[ 0 ], 0x2158 /*『*/ ) ;
    node->mtextbuffer[ 1 ] = node->textbuffer[ node->cur_pos ] ;
    MYCHAR_SET_JISX0208_1983
      ( node->mtextbuffer[ 2 ], 0x2159 /*』*/ ) ;
    break ;
  default :
    break ;
  }
  return 0 ;
}

/*
 * 直接コード入力を行う関数。
 */
int j_input_by_code_or_menu_start
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  j_delete_kanaprefix( node ) ;
  /* 現在のカーソル位置を記憶する。*/
  node->j_input_by_code_or_menu_point = node->cur_pos ;
  /* ミニバッファを利用して、文字列を入力させる。*/
  j_read_string
    ( gw, sbuffer, node, "JIS or EUC code (00nn or CR for Jump Menu): ",
      J_INPUT_BY_CODE_OR_MENU_MODE ) ;
  return 0 ;
}

/*
 * "/" による変換。
 */
int j_abbrev_input
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int rchara )
{
  if( node->j_henkan_mode && !node->j_henkan_on ){
    err_msg( node, "Already in ▽ mode" ) ;
    return 1 ;
  }
  j_kakutei( gw, node ) ;
  j_set_henkan_point_subr( gw, sbuffer, node, rchara ) ;
  node->j_abbrev = True ;
  return 0 ;
}

/*
 * 与えられた位置からカウント量だけカーソルを移動させる関数。
 * ---------
 * そのまま、Control-F の処理に対応している。
 */
int j_forward_char( struct SKKInputNode *node )
{
  /* 仮名の入力中なら確定にしてしまう。*/
  j_delete_kanaprefix( node ) ;
  /* カーソルを移動させる。*/
  if( !IS_END_OF_STRING( node->textbuffer[ node->cur_pos ] ) )
    node->cur_pos ++ ;
  return 0 ;
}

/*
 * 与えられた位置からカウント量だけカーソルを戻す関数。
 * ---------
 * そのまま、Control-B の処理に対応している。
 */
int j_backward_char( struct SKKInputNode *node )
{
  /* 仮名の入力中なら確定にしてしまう。*/
  j_delete_kanaprefix( node ) ;
  /* カーソルを移動させる。*/
  if( node->cur_pos > node->cur_pos_top )
    node->cur_pos -- ;
  return 0 ;
}

/*
 * カーソル位置の文字を抹消する関数。
 *----
 * 備考：特になし。
 */
int j_delete_char( struct SKKInputNode *node )
{
  /* 仮名の入力中なら確定にしてしまう。*/
  j_delete_kanaprefix( node ) ;
  delete_char( node, node->cur_pos, 1 ) ;
  return 0 ;
}

/*
 * カーソルの前のキャラを抹消する関数。
 */
static int delete_backward_char
( Widget gw, struct SKKInputNode *node, XEvent *xevent, int sbflag )
{
  /* カーソルはバッファの先頭まで来ているのですか？ */
  if( node->cur_pos > node->cur_pos_top ){
    /* バッファの先頭までは来ていませんので、一個前に戻せます。*/
    node->cur_pos -- ;
    /* 戻したところで、その場所に表示されていた文字を消します。*/
    delete_char( node, node->cur_pos, 1 ) ;
  } else {
    /* 一番親だったりした場合には、バックスペースを押し戻す。*/
    if( node->parentbuffer == NULL && sbflag ){
      /* さあ、押し戻しましょう。*/
      j_sendback_keypress( gw, xevent ) ;
    }
  }
  return 0 ;
}

/*
 * completion 動作中に一つ前の候補を求める関数。
 *----
 * completion 動作中には、ヒットした要素は全て双方向リストの形で保持さ
 * れている。現在表示している要素の一つ前の要素をそのリストから抜き出
 * して、適切な位置に挿入する。
 */
static int find_previous_completion( struct SKKInputNode *node )
{
  int startpos ;

  /* もはやこれ以上前はなし。*/
  if( node->j_current_completion_vector_index == NULL ){
    err_msg( node, "No previous completions." ) ;
    return 1 ;
  }
  /* 一つ前の候補を選択して…。*/
  node->j_current_completion_vector_index = 
    node->j_current_completion_vector_index->prev ;
  startpos = ( node->j_henkan_start_point < node->cur_pos )?
    node->j_henkan_start_point : node->cur_pos ;
  if( node->j_current_completion_vector_index != NULL ){
    j_delete_region
      ( node, node->j_henkan_start_point, node->cur_pos ) ;
    /* 補完できた文字列を抜き出す。*/
    copyCandidate
      ( temporary_buffer,
	( node->j_current_completion_vector_index )->node,
	( node->j_current_completion_vector_index )->position,
	TEXTMAXLEN ) ;
    j_insert_str( node, startpos, temporary_buffer ) ;
    return 0 ;
  } else {
    /* 補完される前の文字列を表示する…。実はこれでは不十分なのだが…*/
    j_delete_region
      ( node, node->j_henkan_start_point, node->cur_pos ) ;
    j_insert_str( node, startpos, node->j_henkan_key ) ;
    return 0 ;
  }
}

/*
 * comma を代入する関数。
 *-----
 */
int j_insert_comma
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  /* 人類補完計画実行中ならば…。*/
  if( node->j_completion_mode ){
    return find_previous_completion( node ) ;
  }
  return j_self_insert( gw, sbuffer, node, ',' ) ;
}

int j_abbrev_comma( Widget gw, struct SKKInputNode *node )
{
  /* 人類補完計画実行中ならば…。*/
  if( node->j_completion_mode ){
    return find_previous_completion( node ) ;
  }
  return j_insert_chara( node, node->cur_pos, ',' ) ;
}

/*
 * completion 動作中に一つ次の候補を求める関数。
 *----
 * completion 動作中には、ヒットした要素は全て双方向リストの形で保持さ
 * れている。現在表示している要素の一つ次の要素をそのリストから抜き出
 * して、適切な位置に挿入する。
 */
static int find_next_completion( struct SKKInputNode *node )
{
  int startpos ;
  /* 一番先頭の先頭を指しているのなら、最初の補完結果にあわせる。*/
  if( node->j_current_completion_vector_index == NULL ){
    node->j_current_completion_vector_index =
      node->j_completion_vector_index_top ;
  } else {
    /* これ以上次の候補を表示することは可能ですか？ */
    if( node->j_current_completion_vector_index->next == NULL ){
      err_msg( node, "No more completions." ) ;
      return 1 ;
    }
    /* 次の補完結果に移動しましょう。*/
    node->j_current_completion_vector_index =
      node->j_current_completion_vector_index->next ;
  }
  j_delete_region
    ( node, node->j_henkan_start_point, node->cur_pos ) ;
  /* 補完できた文字列を抜き出す。*/
  copyCandidate
    ( temporary_buffer,
      ( node->j_current_completion_vector_index )->node,
      ( node->j_current_completion_vector_index )->position,
      TEXTMAXLEN ) ;
  startpos = ( node->j_henkan_start_point < node->cur_pos )?
    node->j_henkan_start_point : node->cur_pos ;
  j_insert_str( node, startpos, temporary_buffer ) ;
  return 0 ;
}

/*
 * period を代入する関数。
 *-----
 * j_completion があると非常に厄介な処理が入るのだが、今回、その機能の
 * 実装予定はないので、楽。
 */
int j_insert_period
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  /* 人類補完計画実行中ならば…。*/
  if( node->j_completion_mode ){
    return find_next_completion( node ) ;
  }
  return j_self_insert( gw, sbuffer, node, '.' ) ;
}

int j_abbrev_period( Widget gw, struct SKKInputNode *node )
{
  /* 人類補完計画実行中ならば…。*/
  if( node->j_completion_mode ){
    return find_next_completion( node ) ;
  }
  return j_insert_chara( node, node->cur_pos, '.' ) ;
}

int j_date_ad
( struct myChar *buffer, int bufsize, int date_ad, int number_style )
{
  static struct myChar skk_week_alist[] = {
    { CHARSET_JISX0208_1983, 0x467c /*"日"*/ },
    { CHARSET_JISX0208_1983, 0x376e /*"月"*/ },
    { CHARSET_JISX0208_1983, 0x3250 /*"火"*/ },
    { CHARSET_JISX0208_1983, 0x3f65 /*"水"*/ },
    { CHARSET_JISX0208_1983, 0x4c5a /*"木"*/ },
    { CHARSET_JISX0208_1983, 0x3662 /*"金"*/ },
    { CHARSET_JISX0208_1983, 0x455a /*"土"*/ },
  } ;
  time_t tval ;
  int year ;
  struct myChar *dptr ;
  struct tm *lt ;

  /* 1970/1/1からの経過時刻を得る。*/
  if( time( &tval ) == -1 )
    return False ;
  /* 表示方法がデフォルトかどうかを調べる。*/
  if( date_ad < 0 ){
    date_ad = skkinput_date_ad ;
  }
  if( number_style < 0 ){
    number_style = skkinput_number_style ;
  }
  /* time_t から struct tm * に変換する。*/
  lt = localtime( &tval ) ;
  /* 西暦何年の何月何日何曜日であるかを得る。*/
  year = lt->tm_year + 1900 ;
  if( !date_ad && bufsize > 2 ){
    /* 年号を求める。*/
    if( year <= 1867 ){		/*"西暦"*/
      MYCHAR_SET_JISX0208_1983( buffer[ 0 ], 0x403e ) ;
      MYCHAR_SET_JISX0208_1983( buffer[ 1 ], 0x4e71 ) ;
      MYCHAR_SET_END_OF_STRING( buffer[ 2 ] ) ;
    } else if( year < 1911 ){	/*"明治"*/
      year -= 1867 ;
      MYCHAR_SET_JISX0208_1983( buffer[ 0 ], 0x4c4d ) ;
      MYCHAR_SET_JISX0208_1983( buffer[ 1 ], 0x3c23 ) ;
      MYCHAR_SET_END_OF_STRING( buffer[ 2 ] ) ;
    } else if( year < 1925 ){	/*"大正"*/
      year -= 1911 ;
      MYCHAR_SET_JISX0208_1983( buffer[ 0 ], 0x4267 ) ;
      MYCHAR_SET_JISX0208_1983( buffer[ 1 ], 0x4035 ) ;
      MYCHAR_SET_END_OF_STRING( buffer[ 2 ] ) ;
    } else if( year < 1988 ){	/*"昭和"*/
      year -= 1925 ;
      MYCHAR_SET_JISX0208_1983( buffer[ 0 ], 0x3e3c ) ;
      MYCHAR_SET_JISX0208_1983( buffer[ 1 ], 0x4f42 ) ;
      MYCHAR_SET_END_OF_STRING( buffer[ 2 ] ) ;
    } else {			/*"平成"*/
      year -= 1988 ;
      MYCHAR_SET_JISX0208_1983( buffer[ 0 ], 0x4a3f ) ;
      MYCHAR_SET_JISX0208_1983( buffer[ 1 ], 0x402e ) ;
      MYCHAR_SET_END_OF_STRING( buffer[ 2 ] ) ;
    }
    dptr = buffer + 2 ;
    bufsize -= 2 ;
  } else {
    /* 年号は無しになる。*/
    MYCHAR_SET_END_OF_STRING( buffer[ 0 ] ) ;
    dptr = buffer ;
  }
  myCharItoa( temporary_buffer, year ) ;

  if( year == 1 && date_ad ){	    /*"元年"*/
    if( bufsize > 2 ){
      MYCHAR_SET_JISX0208_1983( *dptr, 0x3835 ) ;
      dptr ++ ; bufsize -- ;
      MYCHAR_SET_JISX0208_1983( *dptr, 0x472f ) ;
      dptr ++ ; bufsize -- ;
      MYCHAR_SET_END_OF_STRING( *dptr ) ;
    }
  } else {
    /* 年を展開する。*/
    j_num_exp
      ( temporary_buffer, number_style + '0', dptr, bufsize ) ;
    /* 付け足された文字列の一番最後へと移動する。*/
    while( !IS_END_OF_STRING( *dptr ) ){
      dptr ++ ;
      bufsize -- ;
    }
    if( bufsize > 1 ){
      MYCHAR_SET_JISX0208_1983( *dptr, 0x472f ) ;
      dptr ++ ; bufsize -- ;
    }
  }
  /* 西暦何年の何月何日何曜日であるかを得る。*/
  myCharItoa( temporary_buffer, lt->tm_mon + 1 ) ;
  /* 月を展開する。*/
  j_num_exp( temporary_buffer, number_style + '0', dptr, bufsize ) ;
  /* 付け足された文字列の一番最後へと移動する。*/
  while( !IS_END_OF_STRING( *dptr ) ){
    dptr ++ ;
    bufsize -- ;
  }
  if( bufsize > 1 ){
    MYCHAR_SET_JISX0208_1983( *dptr, 0x376e /*"月"*/ ) ;
    dptr ++ ; bufsize -- ;
  }
  myCharItoa( temporary_buffer, lt->tm_mday ) ;
  /* 日を展開する。後ろの TEXTBUFSIZE は嘘だけど、まああふれないと思う
   * から可です。*/ 
  j_num_exp( temporary_buffer, number_style + '0', dptr, bufsize ) ;
  /* 付け足された文字列の一番最後へと移動する。*/
  while( !IS_END_OF_STRING( *dptr ) ){
    dptr ++ ;
    bufsize -- ;
  }
  if( bufsize > 1 ){
    MYCHAR_SET_JISX0208_1983( *dptr, 0x467c /*"日"*/ ) ;
    dptr ++ ; bufsize -- ;
  }
  /* 曜日を示す。*/
  if( bufsize > 1 ){
    MYCHAR_SET_CHARA( *dptr, '(' ) ;
    dptr ++ ; bufsize -- ;
  }
  if( bufsize > 1 ){
    *dptr ++ = skk_week_alist[ lt->tm_wday ] ;
    bufsize -- ;
  }
  if( bufsize > 1 ){
    MYCHAR_SET_CHARA( *dptr, ')' ) ;
    dptr ++ ; bufsize -- ;
  }
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
  return True ;
}

/*
 * その日の日付を入力する関数。
 */
int j_today( struct SKKInputNode *node )
{
  struct myChar buffer[ TEXTBUFSIZE ] ;

  j_delete_kanaprefix( node ) ;
  if( !j_date_ad( buffer, TEXTBUFSIZE, -1, -1 ) )
    return 1 ;

  /* その文字列を挿入する。*/
  j_insert_str( node, node->cur_pos, buffer ) ;
  return 0 ;
}

/*
 * 指定された範囲内の文字列を半角から全角に変換する関数。
 */
static int j_zenkaku_region
( struct SKKInputNode *node, int startpoint, int endpoint )
{
  struct myChar *sptr, *ptr ;
  int pos ;

  if( startpoint > endpoint ){
    err_msg( node, "Henkan end point must be after henkan start point." ) ;
    return 0 ;
  }
  sptr = node->textbuffer + startpoint ;
  ptr  = temporary_buffer ;
  for( pos = startpoint ; pos < endpoint ; pos ++ ){
    if( IS_END_OF_STRING( *sptr ) )
      break ;
    if( IS_ASCII_CHARA( *sptr ) ){
      if( sptr->chara >= 'a' && sptr->chara <= 'z' ){
	ptr->charset = CHARSET_JISX0208_1983 ;
	ptr->chara   = 0x2361 + ( sptr->chara - 'a' ) ;
	ptr ++ ;
	sptr ++ ;
	continue ;
      }
      if( sptr->chara >= 'A' && sptr->chara <= 'Z' ){
	ptr->charset = CHARSET_JISX0208_1983 ;
	ptr->chara   = 0x2341 + ( sptr->chara - 'A' ) ;
	ptr ++ ;
	sptr ++ ;
	continue ;
      }
    }
    *ptr ++ = *sptr ++ ;
  }
  MYCHAR_SET_END_OF_STRING( *ptr ) ;
  j_delete_region( node, startpoint, endpoint ) ;
  j_insert_str( node, startpoint, temporary_buffer ) ;
  return 0 ;
}

int j_zenkaku_henkan
( Widget gw, struct SKKInputNode *node )
{
  if( node->j_henkan_mode && !node->j_henkan_on ){
    node->j_mode = True ;
    if( node->j_henkan_start_point > node->cur_pos ){
      err_msg( node, "Henkan end point must be after henkan start point." ) ;
      return 1 ;
    }
    j_zenkaku_region( node, node->j_henkan_start_point, node->cur_pos ) ;
    j_kakutei( gw, node ) ;
  }
  return 0 ;
}

/*
 * 指定された領域の平仮名を片仮名に変換する関数である。
 * ----
 * arg に入力されたキーの数とかいうのが emacs-lisp だった時には入っているの
 * ですけれど、この機能のサポートは当分先であるので、無視して作ります。
 */
static int j_katakana_region
( struct SKKInputNode *node, int startpoint, int endpoint, int flag )
{
  struct myChar *sptr, *ptr ;
  int pos, length ;

  length = endpoint - startpoint ;
  if( length < 0 ){
    err_msg( node, "Henkan end point must be after henkan start point." ) ;
    return 0 ;
  }

  sptr = node->textbuffer + startpoint ;
  ptr  = temporary_buffer ;

  for( pos = startpoint ; pos < endpoint ; pos ++ ){
    /* 漢字だった場合には...*/
    switch( sptr->charset ){
    case CHARSET_JISX0208_1978 :
    case CHARSET_JISX0208_1983 :
      ptr->charset = sptr->charset ;
      if( sptr->chara >= 0x2421 && sptr->chara <= 0x2473 ){
	if( sptr->chara == 0x2426 && ( pos + 1 ) < endpoint &&
	    sptr[ 1 ].chara == 0x212b &&
	    ( sptr[ 1 ].charset == CHARSET_JISX0208_1978 ||
	      sptr[ 1 ].charset == CHARSET_JISX0208_1983 ) ){
	  ptr->chara = 0x2574 ; /* ヴ */
	  /* 次の文字を見た分だけ飛ばす。*/
	  sptr ++ ;
	  pos ++ ;
	} else {
	  ptr->chara = sptr->chara + 0x0100 ;
	}
      } else {
	ptr->chara = sptr->chara ;
      }
      sptr ++ ;
      ptr ++ ;
      break ;
    default :
      *ptr ++ = *sptr ++ ;
      break ;
    }
  }
  MYCHAR_SET_END_OF_STRING( *ptr ) ;

  j_delete_region( node, startpoint, endpoint ) ;
  j_insert_str( node, startpoint, temporary_buffer ) ;
  return 0 ;
}

static int j_hiragana_region
( struct SKKInputNode *node, int startpoint, int endpoint, int flag )
{
  struct myChar *sptr, *ptr ;
  int pos, length ;

  length = endpoint - startpoint ;
  if( length < 0 ){
    err_msg( node, "Henkan end point must be after henkan start point." ) ;
    return 0 ;
  }

  sptr = node->textbuffer + startpoint ;
  ptr  = temporary_buffer ;

  for( pos = startpoint ; pos < endpoint ; pos ++ ){
    /* 漢字だった場合には...*/
    switch( sptr->charset ){
    case CHARSET_JISX0208_1978 :
    case CHARSET_JISX0208_1983 :
      ptr->charset = sptr->charset ;
      if( sptr->chara >= 0x2521 && sptr->chara <= 0x2573 ){
	if( sptr->chara == 0x2574 &&
	    ( sptr[ 1 ].charset == CHARSET_JISX0208_1978 ||
	      sptr[ 1 ].charset == CHARSET_JISX0208_1983 ) ){
	  ptr->chara = 0x2426 ; /* う */
	  ptr ++ ;
	  ptr->chara = 0x212b ; /* ゛ */
	} else {
	  ptr->chara = sptr->chara - 0x0100 ;
	}
      } else {
	ptr->chara = sptr->chara ;
      }
      sptr ++ ;
      ptr ++ ;
      break ;
    default :
      *ptr ++ = *sptr ++ ;
      break ;
    }
  }
  MYCHAR_SET_END_OF_STRING( *ptr ) ;

  j_delete_region( node, startpoint, endpoint ) ;
  j_insert_str( node, startpoint, temporary_buffer ) ;
  return 0 ;
}

/*
 * 仮名モードと片仮名モードを切り替える関数。
 *-----
 * ここの引数には「ESC 数字」によるコマンドの入力された数が入って来る。
 * でも、現在その機能をサポートする予定はないので、ここには NULL がず
 * っと入っていることであらう。
 */
int j_toggle_kana
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int arg, int chara )
{
  /* "q" に該当する rule-list が無いか考える。*/
  if( j_kakutei_kana_mode( gw, sbuffer, node, chara ) )
    return 0 ;
  if( node->j_henkan_mode && !node->j_henkan_on ){
    /* 変換作業中ならば、片仮名に変換して返す。送り仮名モードなんて無視。*/
    if( !node->j_katakana_mode ){
      j_katakana_region
	( node, node->j_henkan_start_point, node->cur_pos, True ) ;
    } else {
      j_hiragana_region
	( node, node->j_henkan_start_point, node->cur_pos, True ) ;
    }
    j_kakutei( gw, node ) ;
  } else {
    /* それ以外の場所だったら、片仮名モードと仮名モードを切り替える。*/
    j_kakutei( gw, node ) ;
    node->j_katakana_mode = !node->j_katakana_mode ;
  }
  return 0 ;
}

/*
 * 変換の途中もしくは仮名入力の途中ならば確定の機能を行い、英数モードなら
 * ローマ字仮名入力モードへと移行させる関数。
 */
int j_kanainput_mode_on
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node )
{
  /* 変換の途中もしくは仮名入力の途中であるならば、確定する。*/
  j_kakutei( gw, node ) ;
  j_kakutei_kana_mode( gw, sbuffer, node, '\0' ) ;
  /* 英数モードはまだ考慮していないので、その処理はない。*/
  return 0 ;
}

int j_mode_off
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  if( j_kakutei_kana_mode( gw, sbuffer, node, chara ) )
    return 0 ;
  j_kakutei( gw, node ) ;
  node->j_mode    = False ;
  node->j_zenkaku = False ;
  return 0 ;
}

/*
 * 謎の全角入力モードへと移行させる処理を行う関数である。
 */
int j_zenkaku_eiji
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  if( j_kakutei_kana_mode( gw, sbuffer, node, chara ) )
    return 0 ;
  j_kakutei( gw, node ) ;
  node->j_mode    = False ;
  node->j_zenkaku = True ;
  return 0 ;
}

static int j_start_henkan_sub( Widget gw, struct SKKInputNode *node )
{
  /* 仮名の入力中なら確定にしてしまう。*/
  j_delete_kanaprefix( node ) ;
  /* 変換しようとしている位置は正しいのですか？ */
  if( node->j_henkan_start_point > node->cur_pos ){
    err_msg( node, "Henkan end point must be after henkan start point." ) ;
    return 1 ;
  }
  return 0 ;
}

/*
 * 変換開始関数。
 *----
 * 変換前には必ず一回は呼ばれると予想される関数である。
 */
int j_start_henkan
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  int okuri_is_n, copy_sareta_mojisuu, copy_suru_mojisuu ;
  struct myChar lchara ;

  okuri_is_n =
    ( ( IS_ASCII_EQUAL  ( node->j_prefix[ 0 ], 'n' ) &&
	IS_END_OF_STRING( node->j_prefix[ 1 ] ) )? True : False ) ;

  /* 仮名の入力中なら確定にしてしまう。*/
  j_delete_kanaprefix( node ) ;

  if( !node->j_henkan_mode ){
    /* 変換可能な状態でなかったら、空白記号を挿入するだけの処理しか *
     * しない。*/
    j_insert_chara( node, node->cur_pos, ' ' ) ;
    return 0 ;
  }
  /* 変換可能な状態になっているのならば、変換処理をする。*/
  node->j_mode = True ;
  /* 既に一回以上変換が行われているのであれば、次の候補を探す。*/
  if( node->j_henkan_on ){
    /* さて、変換を行います。*/
    j_henkan( gw, sbuffer, node ) ;
  } else {
    /* 変換開始前の処理。*/
    if( j_start_henkan_sub( gw, node ) )
      return 1 ;
    if( node->j_okurigana_mode && okuri_is_n ){
      MYCHAR_SET_CHARA( lchara, 'n' ) ;
      j_set_okurigana( gw, sbuffer, node, lchara ) ;
    } else {
      /* 現在のカーソル位置が変換終了位置となり申す。*/
      node->j_henkan_end_point = node->cur_pos ;
      /* 変換キーを切り出す。*/
      copy_suru_mojisuu =
	node->j_henkan_end_point - node->j_henkan_start_point ;
      if( node->j_katakana_mode ){
	copy_sareta_mojisuu = myCharHiraKataStrnncpy
	  ( node->j_henkan_key, TEXTMAXLEN,
	    node->textbuffer + node->j_henkan_start_point,
	    copy_suru_mojisuu ) ;
      } else {
	myCharStrncpy
	  ( node->j_henkan_key,
	    node->textbuffer + node->j_henkan_start_point,
	    copy_suru_mojisuu ) ;
	copy_sareta_mojisuu = copy_suru_mojisuu ;
      }
      MYCHAR_SET_END_OF_STRING
	( node->j_henkan_key[ copy_sareta_mojisuu ] ) ;
      /* 単語登録の時に使うキーを保存しておく。*/
      myCharStrcpy( node->j_henkan_key2, node->j_henkan_key ) ;
      /* #文字変換のための処理。*/
      if( skkinput_use_numeric_conversion ){
	j_compute_numeric_henkan_key
	  ( node->j_search_key, node->j_henkan_key, node->j_num_list ) ;
	/* (times) を動かすための処理。*/
	skkinput_create_j_num_list( node->j_num_list ) ;
      } else {
	myCharStrcpy( node->j_search_key, node->j_henkan_key ) ;
	MYCHAR_SET_END_OF_STRING( node->j_num_list[ 0 ] ) ;
      }
      /* さて、変換を行います。*/
      j_henkan( gw, sbuffer, node ) ;
    }
  }
  return 0 ;
}

/*
 * 一つ前の候補へと変換を戻す関数。
 *----
 * 本家本元の skk の同名の関数は動作が今一つ分かんないとこもありますけど、
 * まあ良いですよね。違ったとこが分かってから直します。
 */
int j_previous_candidate
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node )
{
  int pos ;

  if( node->j_henkan_mode && node->j_henkan_on ){
    if( node->j_henkan_count == 0 ){
      /* ここの状態になりうるのだろうか？ */
      /* j_henkan_count = 0 としてしまうと、j_henkan_on が off になってし *
       * まうと思うのだが...。一応、まあ、処理はあると仮定するけど。      */
      return 0 ;
    } else if( node->j_henkan_count == 1 ){
      /* 変換していない状態へ移行する。*/
      node->j_henkan_on     = False ;
      node->j_henkan_count  = 0 ;
      MYCHAR_SET_END_OF_STRING( node->j_num_list[ 0 ] ) ;

      /* 変換終了処理を呼出す。*/
      free_VectorIndex( &( node->j_henkan_vector_index_top ) ) ;
      free_SkkinpSearchVector( &( node->j_henkan_vector_top ) ) ;
      node->j_current_henkan_vector_index = NULL ;

      /* 変換によって書かれていた文字列を消去する。*/
      j_delete_region
	( node, node->j_henkan_start_point, node->j_henkan_end_point ) ;
      node->cur_pos = node->j_henkan_end_point ;
      /* もとの文字列に戻す。*/
      pos = myCharStrlen( node->j_henkan_key ) - 1 ;
      if( IS_ASCII_CHARA( node->j_henkan_key[ pos ] ) &&
	  node->j_henkan_key[ pos ].chara >='a' &&
	  node->j_henkan_key[ pos ].chara <= 'z' &&
	  !node->j_abbrev )
	MYCHAR_SET_END_OF_STRING( node->j_henkan_key[ pos ] ) ;
      j_insert_str( node, node->cur_pos, node->j_henkan_key ) ;
      /* 変換終了位置を元に戻す。*/
      node->j_henkan_end_point = node->cur_pos ;
      /* 「▼」を「▽」に戻す。*/
      j_change_marker_to_write
	( node,	node->j_henkan_start_point - 1 ) ;
    } else {
      /* 一つ前の候補を取り出す。*/
      if( ( node->j_current_henkan_vector_index )->prev != NULL ){
	node->j_current_henkan_vector_index =
	  ( node->j_current_henkan_vector_index )->prev ;
	node->j_henkan_count -- ;
      }
      /* 現在の候補を消す。*/
      j_delete_region
	( node, node->j_henkan_start_point, node->j_henkan_end_point ) ;
      /* 取り出した候補をバッファにうつす。*/
      copyCandidate
	( temporary_buffer,
	 ( node->j_current_henkan_vector_index )->node,
	 ( node->j_current_henkan_vector_index )->position,
	 TEXTMAXLEN ) ;
      node->cur_pos_backup = node->cur_pos ;
      node->cur_pos = node->j_henkan_start_point ;
      /* 候補を書き込む。*/
      j_insert_word
	( node, node->j_henkan_start_point, temporary_buffer ) ;
      node->j_henkan_end_point = node->cur_pos ;
      node->cur_pos = node->cur_pos_backup ;
    }
  } else {
    j_kana_input( gw, sbuffer, node, 'x' ) ;
  }
  return 0 ;
}

/*
 * キーボードからの Control-H の処理を行う関数。
 *----
 * sbflag は kterm などに C-h を送るかどうか。
 * sendflag は確定した時に文字列を変換クライアントに送るかどうか。
 */
int j_backward_delete_char
( Widget gw, struct skkinputBuffer *sbuffer, 
  struct SKKInputNode *node, int sbflag )
{
  if( node->j_henkan_mode && 
      node->cur_pos == node->j_henkan_start_point ){
    /* 変換モードであり、変換開始位置にある場合には、▽を消してしまう *
     * ことになってしまうので、確定することになる。                   */
    node->j_henkan_count = 0 ;
    j_kakutei( gw, node ) ;
  } else if( node->j_henkan_on ){
    /* 変換モードであり、一回以上実際の変換が行われている場合には、   *
     * 前の候補を出すモードがある。                                   */
    if( node->cur_pos == node->j_henkan_end_point &&
	!skkinput_delete_implies_kakutei ){
      j_previous_candidate( gw, sbuffer, node ) ;
    } else {
      int p = node->j_henkan_end_point ;
      /* この delete_backward_char で end_point が移動してしまうため *
       * cur_pos == end_point だった場合には、駄目になってしまうねえ */
      delete_backward_char( gw, node, &sbuffer->xevent, sbflag ) ;
      if( node->cur_pos < p )
	j_kakutei( gw, node ) ;
    }
  } else  {
    /* 送り仮名を入力している途中だったら、それを無効にする。*/
    if( node->j_okurigana_mode ){
      node->j_okurigana_mode = False ;
      MYCHAR_SET_END_OF_STRING( node->j_okuri_chara ) ;
      if( IS_ASCII_EQUAL
	  ( node->textbuffer[ node->j_okurigana_start_point ], '*' ) ){
	/* ええと、"▽み*っっっっっt" で C-h を押すといっぱい消えるよ *
	 * う。*/
	if( node->j_kana_mode == True &&
	    node->j_kana_start_point > node->j_okurigana_start_point ){
	  j_delete_region
	    ( node, node->j_okurigana_start_point,
	      node->j_kana_start_point ) ;
	} else {
	  delete_char( node, node->j_okurigana_start_point, 1 ) ;
	}
      }
      /* 仮名入力の最中ならば、その仮名プレフィクスは全て削除する。*/
      if( node->j_kana_mode ){
	node->j_kana_mode = False ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
	/* 仮名プレフィクスの開始位置からカーソル位置までを削除する。*/
	j_delete_region( node, node->j_kana_start_point, node->cur_pos ) ;
      }
    } else {
      /* 仮名入力の最中ならば、その仮名プレフィクスは全て削除する。*/
      if( node->j_kana_mode ){
	node->j_kana_mode = False ;
	MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
	/* 仮名プレフィクスの開始位置からカーソル位置までを削除する。*/
	j_delete_region( node, node->j_kana_start_point, node->cur_pos ) ;
      } else {
	delete_backward_char( gw, node, &sbuffer->xevent, sbflag ) ;
      }
    }
  }
  return 0 ;
}

/*
 * カーソルを行の先頭へ移動させる関数。
 */
int j_beginning_of_line( struct SKKInputNode *node )
{
  /* 仮名入力の途中なら、そのプレフィクスを削除する。*/
  j_delete_kanaprefix( node ) ;
  /* 行頭へ。*/
  node->cur_pos = node->cur_pos_top ;
  return 0 ;
}

/*
 * カーソルを行の最後へ移動させる関数。
 */
int j_end_of_line( struct SKKInputNode *node )
{
  /* 仮名入力の途中なら、そのプレフィクスを削除する。*/
  j_delete_kanaprefix( node ) ;
  /* 行末へ。*/
  node->cur_pos = myCharStrlen( node->textbuffer ) ;
  return 0 ;
}

/*
 * カーソル位置以降の文字をカットバッファに移動させる関数。
 */
int j_kill_line( struct SKKInputNode *node, struct myChar *cutbuffer )
{
  int last_pos ;
  /* 仮名入力の途中なら、そのプレフィクスを削除する。*/
  j_delete_kanaprefix( node ) ;

  last_pos = myCharStrlen( node->textbuffer ) ;
  /* カットバッファにコピーする。*/
  myCharStrcpy( cutbuffer, node->textbuffer + node->cur_pos ) ;
  /* カーソル以降の文字を消去する。*/
  j_delete_region( node, node->cur_pos, last_pos ) ;
  return 0 ;
}

/*
 * カットバッファに入っている文字列を貼り付ける関数。
 *----------------
 * うげげげげげ、カットバッファをウィジェットが持ってるのか…そりゃ大
 * 変だ…しまったな。どうにかひっぺがさないと…ウィジェットが持つのは
 * 正しいのだけど、ウィジェットをこいつらに渡すのはちょっと…。
 */
int j_yank( struct SKKInputNode *node, struct myChar *cutbuffer )
{
  j_insert_str( node, node->cur_pos, cutbuffer ) ;
  return 0 ;
}

/*
 * カーソル位置の一つ前のキャラとカーソル位置のキャラを交換してカーソ
 * ルを一つ進める処理を行う関数。
 *-----
 * ただし、カーソルが行末だった場合にはカーソルの移動はなく、交換され
 * るキャラはカーソルの一つ前ともう一つ前である。
 *(注意)
 * EUC-3byte コードは無視してます。
 */
int j_transpose_chars( struct SKKInputNode *node )
{
  struct myChar transpose_buffer[ 3 ] ;

  /* 仮名入力の途中なら、そのプレフィクスを削除する。*/
  j_delete_kanaprefix( node ) ;
  /* 行頭なら何もしないで抜ける。*/
  if( node->cur_pos == node->cur_pos_top ){
    err_msg( node, "Beginning of buffer" ) ;
    return 0 ;
  } else if( IS_END_OF_STRING( node->textbuffer[ node->cur_pos ] ) )
    return 0 ;
  /* 交換する文字を抜き出して文字列にしておく。*/
  transpose_buffer[ 0 ] = node->textbuffer[ node->cur_pos     ] ;
  transpose_buffer[ 1 ] = node->textbuffer[ node->cur_pos - 1 ] ;
  MYCHAR_SET_END_OF_STRING( transpose_buffer[ 2 ] ) ;
  /* 交換される領域を消去する。*/
  j_delete_region( node, node->cur_pos - 1, node->cur_pos + 1 ) ;
  /* 交換後の文字列を挿入する。*/
  j_insert_str( node, node->cur_pos, transpose_buffer ) ;
  return 0 ;
}

static int search_previous_history
( struct myChar *buffer, int spos, int nowpos )
{
  nowpos -- ;
  do {
    nowpos -- ;
    if( nowpos < 0 )
      nowpos += TEXTBUFSIZE ;
  } while( !IS_END_OF_STRING( buffer[ nowpos ] ) && nowpos != spos ) ;

  /* 一つ前の候補の開始位置。*/
  if( nowpos != spos ){
    nowpos = ( nowpos + 1 ) % TEXTBUFSIZE ;
  }
  return nowpos ;
}

/*
 * 前の行の戻る…って、エディタじゃないから一個前の入力が残っていれば
 * そちらを表示する関数。
 *-----
 * というわけで、入力途中のものは確定されてしまい、ヒストリの一番最後
 * に保存される。( なくなってしまったら、ちょっと詐欺だから。)
 */
int j_previous_line
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  struct myChar *ptr ;
  int pos, length, prev_j_mode, prev_j_zenkaku ;
  /* 一番親のバッファでしか有効でないとする。*/
  if( node->parentbuffer != NULL || sbuffer->historybuffer == NULL ||
      sbuffer->hist_start == sbuffer->hist_end ||
      sbuffer->overthespot_like_input ){
    /* 仮名入力の途中なら、そのプレフィクスを削除しておく、そうしなけ
       れば、仮名入力の途中では C-p も C-n も効果なくなってしまうから */
    j_delete_kanaprefix( node ) ;
    return 1 ;
  }
  /* 確定してしまうと、j-mode が変更されてしまうので覚えておく。*/
  prev_j_mode    = node->j_mode ;
  prev_j_zenkaku = node->j_zenkaku ;
  /* 確定する。こうしないわやになる。*/
  j_kakutei( gw, node ) ;
  j_kakutei_kana_mode( gw, sbuffer, node, '\0' ) ;
  /* これ以上前の候補はないので、何もしないで帰る。*/
  if( sbuffer->hist_cur == sbuffer->hist_start ){
    node->j_mode    = prev_j_mode ;
    node->j_zenkaku = prev_j_zenkaku ;
    return 0 ;
  }
  /* 今回の御利用が最初だった場合です。*/
  if( sbuffer->hist_cur < 0 || sbuffer->hist_cur >= TEXTBUFSIZE ){
    /* 現在の入力中の文字列を保存しておく。*/
    myCharStrcpy( sbuffer->histcurbackbuffer, node->textbuffer ) ;
    sbuffer->hist_cur = sbuffer->hist_end ;
  }
  /* 一つ前の候補を求める。*/
  sbuffer->hist_cur = search_previous_history
    ( sbuffer->historybuffer, sbuffer->hist_start, sbuffer->hist_cur ) ;
  /* 候補を抜き出す。*/
  ptr = node->textbuffer ;
  pos = sbuffer->hist_cur ;
  length = 0 ;
  while( !IS_END_OF_STRING( sbuffer->historybuffer[ pos ] ) ){
    *ptr ++ = sbuffer->historybuffer[ pos ++ ] ;
    length ++ ;
    if( pos >= TEXTBUFSIZE )
      pos = 0 ;
  }
  MYCHAR_SET_END_OF_STRING( *ptr ) ;
  /* カーソル位置の修正。*/
  node->cur_pos = length ;
  /* 以前の j-mode に戻す。*/
  node->j_mode    = prev_j_mode ;
  node->j_zenkaku = prev_j_zenkaku ;
  return 0 ;
}

static int search_next_history
( struct myChar *buffer, int epos, int nowpos )
{
  do {
    nowpos ++ ;
    if( nowpos >= TEXTBUFSIZE )
      nowpos = 0 ;
  } while( !IS_END_OF_STRING( buffer[ nowpos ] ) && nowpos != epos ) ;

  /* 一つ前の候補の開始位置。*/
  if( nowpos != epos ){
    nowpos = ( nowpos + 1 ) % TEXTBUFSIZE ;
  }
  return nowpos ;
}

/*
 * 次の行へ行く…って、エディタじゃないから一個前に戻っていた時にもと
 * に戻したい時に利用するわけです。
 *-----
 * というわけで、入力途中のものは確定されてしまい、ヒストリの一番最後
 * に保存される。( なくなってしまったら、ちょっと詐欺だから。)
 */
int j_next_line
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  struct myChar *sptr, *dptr ;
  int pos, length, prev_j_mode, prev_j_zenkaku ;

  /* 一番親のバッファでしか有効でないとする。*/
  if( node->parentbuffer != NULL || sbuffer->historybuffer == NULL ||
      sbuffer->hist_cur < 0 || sbuffer->overthespot_like_input ){
    /* 仮名入力の途中なら、そのプレフィクスを削除しておく、そうしなけ
       れば、仮名入力の途中では C-p も C-n も効果なくなってしまうから */
    j_delete_kanaprefix( node ) ;
    return 1 ;
  }
  /* 確定してしまうと、j-mode が変更されてしまうので覚えておく。*/
  prev_j_mode    = node->j_mode ;
  prev_j_zenkaku = node->j_zenkaku ;
  /* まず確定する。*/
  j_kakutei( gw, node ) ;
  j_kakutei_kana_mode( gw, sbuffer, node, '\0' ) ;
  /* 次の候補を捜す。*/
  sbuffer->hist_cur = search_next_history
    ( sbuffer->historybuffer, sbuffer->hist_end, sbuffer->hist_cur ) ;
  /* 今回一番最後を指しているってことは？ */
  if( sbuffer->hist_cur == sbuffer->hist_end ){
    myCharStrcpy( node->textbuffer, sbuffer->histcurbackbuffer ) ;
    node->cur_pos = myCharStrlen( node->textbuffer ) ;
    sbuffer->hist_cur = -1 ;
    /* 以前のモードに戻しておく。ただし、変換途中とかそういう情報は落
       ちている。*/ 
    node->j_mode    = prev_j_mode ;
    node->j_zenkaku = prev_j_zenkaku ;
    return 0 ;
  }
  /* 候補を抜き出す。*/
  pos  = sbuffer->hist_cur ;
  sptr = sbuffer->historybuffer + pos ;
  dptr = node->textbuffer ;
  length = 0 ;
  while( !IS_END_OF_STRING( *sptr ) && pos != sbuffer->hist_end ){
    *dptr ++ = *sptr ++ ;
    pos ++ ;
    length ++ ;
    if( pos >= TEXTBUFSIZE ){
      sptr = sbuffer->historybuffer ;
      pos  = 0 ;
    }
  } 
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
  /* カーソル位置を修正。*/
  node->cur_pos = length ;
  /* 以前のモードに戻しておく。ただし、変換途中とかそういう情報は落
     ちている。*/ 
  node->j_mode    = prev_j_mode ;
  node->j_zenkaku = prev_j_zenkaku ;
  return 0 ;
}

/*
 * 変換開始位置を設定する関数。
 *----
 * 大文字の入力により変換開始位置が設定されることにデフォルトではなっ
 * ている。
 */
void j_set_henkan_point
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  switch( chara ){
  case 'A' :
    /* 母音を入力することにより変換開始位置が設定される場合の処理。*/
    j_set_henkan_point_ByVowel
      ( gw, sbuffer, node, skk_roma_kana_a, chara ) ;
    break ;
  case 'E' :
    /* 母音を入力することにより変換開始位置が設定される場合の処理。*/
    j_set_henkan_point_ByVowel
      ( gw, sbuffer, node, skk_roma_kana_e, chara ) ;
    break ;
  case 'I' :
    /* 母音を入力することにより変換開始位置が設定される場合の処理。*/
    j_set_henkan_point_ByVowel
      ( gw, sbuffer, node, skk_roma_kana_i, chara ) ;
    break ;
  case 'O' :
    /* 母音を入力することにより変換開始位置が設定される場合の処理。*/
    j_set_henkan_point_ByVowel
      ( gw, sbuffer, node, skk_roma_kana_o, chara ) ;
    break ;
  case 'U' :
    /* 母音を入力することにより変換開始位置が設定される場合の処理。*/
    j_set_henkan_point_ByVowel
      ( gw, sbuffer, node, skk_roma_kana_u, chara ) ;
    break ;
    /* 特殊な見出し文字によって変換の開始位置が設定される場合。*/
  case '<' :
  case '>' :
  case '?' :
    j_set_henkan_point_by_special_midashi_char
      ( gw, sbuffer, node, chara ) ;
    break ;
  default :
    j_set_henkan_point_ByConsonat( gw, sbuffer, node, chara ) ;
    break ;
  }
  return ;
}

void j_set_henkan_point_subr
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int rchara )
{
  struct myChar lchara ;
  /* 仮名入力中かつ"n"なら"ん"にしてしまう。*/
  if( j_kakutei_kana_mode( gw, sbuffer, node, rchara ) )
    return ;
  /* 何だか実際と違うようだけど、こうしておく。後で修正しよう。*/
  if( node->j_henkan_mode )
    j_kakutei( gw, node ) ;
  /* ▽を表示して。*/
  MYCHAR_SET_JISX0208_1983( lchara, 0x2226 /*"▽"*/ ) ;
  j_insert_myChara( node, node->cur_pos, lchara ) ;
  /* 変換開始位置をカーソル位置に決定して。*/
  node->j_henkan_start_point = node->cur_pos ;
  node->j_henkan_mode = True ;
  node->j_henkan_on   = False ;
  return ;
}

/*
 * C-g によって変換「▼」の状態が「▽」の状態へと遷移する場合の処理を
 * 行う関数。
 */
static void j_keyboard_quit_henkanrestart
( Widget gw, struct SKKInputNode *node, int flag )
{
  int endp_is_bigger_than_startp ;
  /* 変換していない状態へ移行する。*/
  node->j_henkan_on      = False ;
  node->j_henkan_count   = 0 ;
  node->j_henkan_show_candidate_mode = False ;
  /* 変換終了処理を呼出す。*/
  free_VectorIndex( &( node->j_henkan_vector_index_top ) ) ;
  free_SkkinpSearchVector( &( node->j_henkan_vector_top ) ) ;
  node->j_current_henkan_vector_index = NULL ;
  /* 変換キーを抹消します。*/
  node->cur_pos2 = node->j_kana_start_point ;
  node->cur_pos3 = node->j_okurigana_start_point ;

  if( node->j_okurigana_mode ){
    /* どうも良く分からないけど…アドホックな対応だけど…。*/
    if( !IS_ASCII_EQUAL
	( node->textbuffer[ node->j_okurigana_start_point ], '*' ) )
      node->j_okurigana_mode = False ;
    endp_is_bigger_than_startp = node->j_henkan_start_point <
      node->j_henkan_end_point ;
    MYCHAR_SET_END_OF_STRING
      ( node->j_henkan_key[ node->j_okurigana_offset ] ) ;
    j_delete_region
      ( node, node->j_henkan_start_point, node->j_okurigana_start_point ) ;
  } else {
#if 0
    pos = myCharStrlen( node->j_henkan_key ) ;
    MYCHAR_SET_END_OF_STRING( node->j_henkan_key[ pos ] ) ;
#endif
    endp_is_bigger_than_startp = False ;
    j_delete_region
      ( node, node->j_henkan_start_point, node->j_henkan_end_point ) ;
  }
  if( !flag ){
    node->cur_pos4 = node->j_henkan_end_point ;
    if( !node->j_katakana_mode ){
      j_insert_str( node, node->j_henkan_end_point, node->j_henkan_key ) ;
    } else {
      myCharKatakanaStrcpy
	( temporary_buffer, node->j_henkan_key ) ;
      j_insert_str( node, node->j_henkan_end_point, temporary_buffer ) ;
    }
    /* 変換終了位置を元に戻す。*/
    node->j_henkan_end_point = node->cur_pos4 ;
  } else if( endp_is_bigger_than_startp ){
    if( !node->j_katakana_mode ){
      j_insert_str
	( node, node->j_henkan_start_point, node->j_henkan_key ) ;
    } else {
      myCharKatakanaStrcpy
	( temporary_buffer, node->j_henkan_key ) ;
      j_insert_str( node, node->j_henkan_start_point, temporary_buffer ) ;
    }
  }
  node->j_kana_start_point = node->cur_pos2 ;
  node->j_okurigana_start_point = node->cur_pos3 ;
  /* 「▼」を「▽」に戻す。*/
  j_change_marker_to_write
    ( node, node->j_henkan_start_point - 1 ) ;
  return ;
}

/*
 * C-g によって変換「▽」の状態が終了する場合の処理を行う関数。
 */
static void j_keyboard_quit_henkanclose
( Widget gw, struct SKKInputNode *node )
{
  int pos ;

  /* 変換モードから抜け出す。*/
  node->j_henkan_mode    = False ;
  node->j_henkan_on      = False ;
  node->j_abbrev         = False ;
  node->j_henkan_show_candidate_mode = False ;
  pos = node->j_henkan_start_point - 1 ;
  /* 変換開始位置に「▼」はあるのか？ 無かったら、変。*/
  if( node->textbuffer[ pos ].charset == CHARSET_JISX0208_1983 &&
      node->textbuffer[ pos ].chara == 0x2226 /*"▽"*/ ){
    delete_char( node, pos, 1 ) ;
  } else {
    err_msg( node, "It seems that you have deleted ▽." ) ;
  }
  /* 送り仮名モードだった場合の対処コード。*/
  if( node->j_okurigana_mode && 
      IS_ASCII_EQUAL
      ( node->textbuffer[ node->j_okurigana_start_point ], '*' ) )
    delete_char( node, node->j_okurigana_start_point, 1 ) ;
  /* 変換開始位置からカーソル位置までを消去する。*/
  j_delete_region
    ( node, node->j_henkan_start_point, node->cur_pos ) ;
  node->j_okurigana_mode = False ;
  return ;
}

/*
 * Control-G を押した時の処理。
 */
void j_keyboard_quit
( Widget gw, struct skkinputBuffer *buffer, struct SKKInputNode *node )
{
  /* 仮名入力中なら入力中のプレフィクスをキャンセルしてしまう。*/
  if( node->j_kana_mode ){
    /* 入力中の仮名プレフィクスを抹消する。*/
    delete_char
      ( node, node->j_kana_start_point,
	node->cur_pos - node->j_kana_start_point ) ;
    node->j_kana_mode = False ;
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
    return ;
  }
  /* 変換モードに移動しているのですか？ */
  if( node->j_henkan_mode ){
    /* 実際に変換は一度以上行われているのですか？ */
    if( node->j_henkan_on ){
      /* 「▼」から「▽」へと状態遷移して、変換の最初へと戻す。*/
      j_keyboard_quit_henkanrestart( gw, node, False ) ;
      node->j_okurigana_mode = False ;
    } else {
      /* 「▽」を消去して変換を終了する。*/
      j_keyboard_quit_henkanclose( gw, node ) ;
    }
    return ;
  }

  /* 直接コード入力モードだった場合の処理。*/
  if( node->j_input_by_code_or_menu_mode ){
    node->j_input_by_code_or_menu_mode = False ;
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
    return ;
  }

  /* 以上のどれでもなかったら、mini-buffer mode にいなかったかどうか *
   * 判定する。*/
  if( node->parentbuffer != NULL ){
    struct SKKInputNode *pNode = node->parentbuffer ;
    int usage = node->j_minibuff_usage ;
    /* ミニバッファの利用は停止されるので、クリアする。*/
    XtVaSetValues( gw, XtNclearMinibuffer, True, NULL ) ;
    /* キャンセルということで、今のノードは解放されてしまいます。*/
    free_Minibuffer( gw, buffer, node ) ;
    /* 親にカーソルを返しましょう。*/
    pNode->cur_exist = True ;

    /* ここで…親が何をしていたのかを判断して適切な処理を親にも実行する。*/
    switch( usage ){
      /* ミニバッファでの変換に移っていた場合…。*/
    case J_HENKAN_IN_MINIBUFF_MODE :
      /* ミニバッファでの変換モードに入っていながら、ちっともヒット *
       * するものが無かった場合の処理。*/
      if( pNode->j_henkan_count == 0 && pNode->j_henkan_mode &&
	  pNode->j_henkan_on ){
	/* ▼のままではなく▽へと状態遷移させる。*/
	j_keyboard_quit_henkanrestart
	  ( gw, pNode, pNode->j_okurigana_mode ) ;
      }
      break ;
    case J_INPUT_BY_CODE_OR_MENU_MODE :
      pNode->j_input_by_code_or_menu_mode = False ;
      MYCHAR_SET_END_OF_STRING( pNode->mtextbuffer[ 0 ] ) ;
      break ;
      /* パージしなかった場合には無事親に復帰できなきゃならん…。確 *
       * 定とかしちゃ駄目…これでどうにかなるのかねえ。*/
    case J_PURGE_YES_OR_NO_P :
      MYCHAR_SET_END_OF_STRING( pNode->mtextbuffer[ 0 ] ) ;
      break ;
    default :
      break ;
    }
    /* 親が変換候補の表示モードでなかったら、空にしておく。*/
    if( !pNode->j_henkan_show_candidate_mode )
      MYCHAR_SET_END_OF_STRING( pNode->mtextbuffer[ 0 ] ) ;
    return ;
  }
#if 0
  XBell( XtDisplay( gw ), 2 ) ;
#endif
  err_msg( node, "Quit" ) ;
  return ;
}

/*
 * 候補選択の処理を行っている場合のキーイベントの処理関数。
 *----
 * ここはキーバインドを変更することはできない。元の skk.el でもそうなっ
 * ている筈。別に変更できるようにしても構わないけどね。
 */
int do_Function_jhenkanShowCandidateMode
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  VectorIndex *vNode ;
  struct keyBuffer *keybuf ;
  int h_count, kouho_num, i ;

  /* エラーの表示で消されてしまっているかもしれない候補を再表示する */
  /* しかし、Emacs は自動回復しているみたいだけど…？ */
  vNode     = j_henkan_show_candidates( node, &h_count ) ;
  kouho_num = h_count - node->j_henkan_count ;
  keybuf    = &sbuffer->keybuf ;

  switch( keybuf->buffer[ keybuf->usage - 1 ] ){
    /* 一番左の候補を選択する。*/
  case 'a' :
    /* 候補を抜き出して…。*/
    copyCandidate
      ( temporary_buffer,
	( node->j_current_henkan_vector_index )->node,
	( node->j_current_henkan_vector_index )->position,
	TEXTMAXLEN ) ;
    /* 変換終了位置を調整。*/
    node->cur_pos_backup = node->cur_pos ;
    node->cur_pos        = node->j_henkan_start_point ;
    /* 文字列の挿入。*/
    j_insert_word
      ( node, node->j_henkan_start_point, temporary_buffer ) ;
    node->j_henkan_end_point = node->cur_pos ;
    node->cur_pos = node->cur_pos_backup ;
    /* このモードの終了。*/
    j_kakutei( gw, node ) ;
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
    break ;

    /* 左から２番目の候補を選択する。*/
  case 's' :
    if( kouho_num < 2 )
      break ;

    node->j_current_henkan_vector_index =
      node->j_current_henkan_vector_index->next ;
    
    /* 候補を抜き出して…。*/
    copyCandidate
      ( temporary_buffer,
	( node->j_current_henkan_vector_index )->node,
	( node->j_current_henkan_vector_index )->position,
	TEXTMAXLEN ) ;
    /* 変換終了位置を調整。*/
    node->cur_pos_backup = node->cur_pos ;
    node->cur_pos        = node->j_henkan_start_point ;
    /* 文字列の挿入。*/
    j_insert_word
      ( node, node->j_henkan_start_point, temporary_buffer ) ;
    node->j_henkan_end_point = node->cur_pos ;
    node->cur_pos = node->cur_pos_backup ;
    /* このモードの終了。*/
    j_kakutei( gw, node ) ;
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
    break ;

    /* 左から３番目の候補を選択する。*/
  case 'd' :
    if( kouho_num < 3 )
      break ;

    node->j_current_henkan_vector_index =
      node->j_current_henkan_vector_index->next->next ;

    /* 候補を抜き出して…。*/
    copyCandidate
      ( temporary_buffer,
	( node->j_current_henkan_vector_index )->node,
	( node->j_current_henkan_vector_index )->position,
	TEXTMAXLEN ) ;
    /* 変換終了位置を調整。*/
    node->cur_pos_backup = node->cur_pos ;
    node->cur_pos        = node->j_henkan_start_point ;
    /* 文字列の挿入。*/
    j_insert_word
      ( node, node->j_henkan_start_point, temporary_buffer ) ;
    node->j_henkan_end_point = node->cur_pos ;
    node->cur_pos = node->cur_pos_backup ;
    /* このモードの終了。*/
    j_kakutei( gw, node ) ;
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
    break ;

    /* 左から４番目の候補を選択する。*/
  case 'f' :
    if( kouho_num < 4 )
      break ;

    node->j_current_henkan_vector_index =
      node->j_current_henkan_vector_index->next->next->next ;

    /* 候補を抜き出して…。*/
    copyCandidate
      ( temporary_buffer,
	( node->j_current_henkan_vector_index )->node,
	( node->j_current_henkan_vector_index )->position,
	TEXTMAXLEN ) ;
    /* 変換終了位置を調整。*/
    node->cur_pos_backup = node->cur_pos ;
    node->cur_pos        = node->j_henkan_start_point ;
    /* 文字列の挿入。*/
    j_insert_word
      ( node, node->j_henkan_start_point, temporary_buffer ) ;
    node->j_henkan_end_point = node->cur_pos ;
    node->cur_pos = node->cur_pos_backup ;
    /* このモードの終了。*/
    j_kakutei( gw, node ) ;
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
    break ;

    /* 左から５番目の候補を選択する。*/
  case 'j' :
    if( kouho_num < 5 )
      break ;

    node->j_current_henkan_vector_index =
      node->j_current_henkan_vector_index->next->next->next->next ;

    /* 候補を抜き出して…。*/
    copyCandidate
      ( temporary_buffer,
	( node->j_current_henkan_vector_index )->node,
	( node->j_current_henkan_vector_index )->position,
	TEXTMAXLEN ) ;
    /* 変換終了位置を調整。*/
    node->cur_pos_backup = node->cur_pos ;
    node->cur_pos        = node->j_henkan_start_point ;
    /* 文字列の挿入。*/
    j_insert_word
      ( node, node->j_henkan_start_point, temporary_buffer ) ;
    node->j_henkan_end_point = node->cur_pos ;
    node->cur_pos = node->cur_pos_backup ;
    /* このモードの終了。*/
    j_kakutei( gw, node ) ;
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
    break ;

    /* 左から６番目の候補を選択する。*/
  case 'k' :
    if( kouho_num < 6 )
      break ;

    node->j_current_henkan_vector_index =
      node->j_current_henkan_vector_index->next->next->next->next->next ;

    /* 候補を抜き出して…。*/
    copyCandidate
      ( temporary_buffer,
	( node->j_current_henkan_vector_index )->node,
	( node->j_current_henkan_vector_index )->position,
	TEXTMAXLEN ) ;
    /* 変換終了位置を調整。*/
    node->cur_pos_backup = node->cur_pos ;
    node->cur_pos        = node->j_henkan_start_point ;
    /* 文字列の挿入。*/
    j_insert_word
      ( node, node->j_henkan_start_point, temporary_buffer ) ;
    node->j_henkan_end_point = node->cur_pos ;
    node->cur_pos = node->cur_pos_backup ;
    /* このモードの終了。*/
    j_kakutei( gw, node ) ;
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
    break ;

    /* 左から７番目の候補を選択する。*/
  case 'l' :
    if( kouho_num < 7 )
      break ;

    node->j_current_henkan_vector_index =
      node->j_current_henkan_vector_index->next->next->next->next->next->next ;

    /* 候補を抜き出して…。*/
    copyCandidate
      ( temporary_buffer,
	( node->j_current_henkan_vector_index )->node,
	( node->j_current_henkan_vector_index )->position,
	TEXTMAXLEN ) ;
    /* 変換終了位置を調整。*/
    node->cur_pos_backup = node->cur_pos ;
    node->cur_pos        = node->j_henkan_start_point ;
    /* 文字列の挿入。*/
    j_insert_word
      ( node, node->j_henkan_start_point, temporary_buffer ) ;
    node->j_henkan_end_point = node->cur_pos ;
    node->cur_pos = node->cur_pos_backup ;
    /* このモードの終了。*/
    j_kakutei( gw, node ) ;
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
    break ;

    /* 一つ前の候補を表示する。*/
  case 'x' :
    /* 候補を列挙するモードを終了しなければならないのか？ */
    if( node->j_henkan_count == 5 ){
      /* はい、終了しなくてはなりません。*/
      node->j_current_henkan_vector_index = 
	( node->j_current_henkan_vector_index )->prev ;
      node->j_henkan_count -- ;
      /* 候補を抜き出して…。*/
      copyCandidate
	( temporary_buffer,
	  ( node->j_current_henkan_vector_index )->node,
	  ( node->j_current_henkan_vector_index )->position,
	  TEXTMAXLEN ) ;
      /* 変換終了位置を調整。*/
      node->cur_pos_backup = node->cur_pos ;
      node->cur_pos        = node->j_henkan_start_point ;
      /* 文字列の挿入。*/
      j_insert_word
	( node, node->j_henkan_start_point, temporary_buffer ) ;
      node->j_henkan_end_point = node->cur_pos ;
      node->cur_pos = node->cur_pos_backup ;
      /* このモードの終了。*/
      node->j_henkan_show_candidate_mode = False ;
      MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
    } else {
      /* いいえ、終了しなくても構わないです。*/
      node->j_henkan_count -= 7 ;
      /* 七つ前の候補を表示すべく戻す。*/
      for( i = 0 ; i < 7 ; i ++ )
	node->j_current_henkan_vector_index = 
	  ( node->j_current_henkan_vector_index )->prev ;
      /* 表示し直す。*/
      j_henkan_show_candidates( node, NULL ) ;
    }
    break ;
    /* 次の候補を表示する。*/
  case ' ' :
    if( vNode != NULL ){
      /* 一番先頭の候補を指しているポインタを移動させる。*/
      node->j_current_henkan_vector_index = vNode ;
      node->j_henkan_count                = h_count ;
      /* 表示し直す。*/
      j_henkan_show_candidates( node, NULL ) ;
    } else {
      /* minibuffer を開いての変換作業に移動する。*/
      skkinput_j_henkan_in_minibuff_init( gw, sbuffer, node ) ;
    }
    break ;
    /* Keyboard-Quit が入った場合の処理。*/
  case 0x07 :
    j_keyboard_quit( gw, sbuffer, node ) ;
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
    break ;
    /* 画面の書き直し。*/
  case 0x0c :
    /* 表示し直す。*/
    j_henkan_show_candidates( node, NULL ) ;
    break ;
    /* 不正なキー入力があった場合。*/
  default :
    err_msg( node, "`%s' is not valid here!",
	     charaToString( keybuf->buffer[ keybuf->usage - 1 ] ) ) ;
    break ;
  }
  return 0 ;
}

static int lookup_at_string( char *string, int chara )
{
  char *ptr = string ;
  int i ;

  for( i = 0 ; *ptr != '\0' ; ptr ++, i ++ ){
    if( *ptr == chara )
      return i ;
  }
  return (-1) ;
}

/*
 * 直接コード入力の時のミニバッファの表示を作る関数。
 */
static void j_input_by_code_or_menu_jmp_showCandidate
( struct SKKInputNode *node, int code )
{
  struct myChar *ptr ;
  int i ;
  /* これを毎回実行する必要があるのかどうかは疑問。initialize でやっ *
   * てしまうべき。*/
  j_input_by_code_menu1_codes[ 0 ] = j_code_n1_min ;

  /* 正しい範囲内にあるかどうかを判断する。*/
  if( code < j_code_n1_min )
    code = j_code_n1_min ;

  ptr = node->mtextbuffer ;
  /* メニューをつくる。*/
  for( i = 0 ; i < 6 ; i ++ ){
    /* くれぐれも code menu keys に漢字を入れないように。*/
    MYCHAR_SET_CHARA
      ( *ptr, upcase_char( j_input_by_code_menu_keys1[ i ] ) ) ;
    ptr ++ ;
    ptr->charset = node->j_input_by_code_current_charset ;
    ptr->chara   = ( code << 8 ) |
      j_input_by_code_menu1_codes[ i ] ;
    ptr ++ ;
    MYCHAR_SET_CHARA( *ptr, ' ' ) ;
    ptr ++ ;
  }
  code ++ ;
  /* 正しい範囲内にあるかどうかを判断する。*/
  if( code > j_code_n1_max )
    code = j_code_n1_min ;

  /* メニューをつくる。(つづき)*/
  for( i = 0 ; i < 6 ; i ++ ){
    /* くれぐれも code menu keys に漢字を入れないように。*/
    MYCHAR_SET_CHARA
      ( *ptr, upcase_char( j_input_by_code_menu_keys1[ i + 6 ] ) ) ;
    ptr ++ ;
    ptr->charset = node->j_input_by_code_current_charset ;
    ptr->chara   = ( code << 8 ) |
      j_input_by_code_menu1_codes[ i ] ;
    ptr ++ ;
    MYCHAR_SET_CHARA( *ptr, ' ' ) ;
    ptr ++ ;
  }
  MYCHAR_SET_END_OF_STRING( *ptr ) ;
  return ;
}

static void j_input_by_code_or_menu_1_jmp_showCandidate
( struct SKKInputNode *node, int code )
{
  unsigned int n1, n2, i ;
  struct myChar *ptr ;

  n1 = ( code >> 8 ) ;
  n2 = ( code & 0x00FF ) ;

#if 0
  fprintf( stderr, "code, n1, n2 = %x, %x, %x\n", code, n1, n2 ) ;
#endif
  ptr = node->mtextbuffer ;

  for( i = 0 ; i < 16 ; i ++ ){
    MYCHAR_SET_CHARA
      ( *ptr, upcase_char( j_input_by_code_menu_keys2[ i ] ) ) ;
    ptr ++ ;
    ptr->charset = node->j_input_by_code_current_charset ;
    ptr->chara   = ( n1 << 8 ) | n2 ;
    ptr ++ ;
    MYCHAR_SET_CHARA( *ptr, ' ' ) ;
    ptr ++ ;

    n2 ++ ;
    if( n2 > j_code_n2_max )
      n2 = j_code_n2_min ;
    if( n2 == j_code_n2_min ){
      n1 ++ ;
      if( n1 > j_code_n1_max )
	n1 = j_code_n1_min ;
    }
  }
  MYCHAR_SET_END_OF_STRING( *ptr ) ;
  return ;
}

int do_function_j_input_by_code_or_menu_jump
( Widget gw, struct skkinputBuffer *buffer, struct SKKInputNode *node )
{
  struct keyBuffer *keybuf ;
  unsigned char kbuf[ 3 ] ;
  int func_no ;

  /* エラーの表示で消されてしまっているかもしれない候補を再表示する */
  /* しかし、Emacs は自動回復しているみたいだけど…？ */
  j_input_by_code_or_menu_jmp_showCandidate
    ( node, node->j_input_by_code_current_code ) ;

  keybuf  = &buffer->keybuf ;
  func_no = lookup_at_string
    ( j_input_by_code_menu_keys1, keybuf->buffer[ keybuf->usage - 1 ] ) ;
  
  if( func_no < 0 ){
    switch( keybuf->buffer[ keybuf->usage - 1 ] ){
      /* 一つ前の候補を表示する。*/
    case 'x' :
      node->j_input_by_code_current_code -= 2 ;
      if( node->j_input_by_code_current_code < j_code_n1_min )
	node->j_input_by_code_current_code = j_code_n1_max ;
      break ;
      /* １つ次の候補を表示する。*/
    case ' ' :
      node->j_input_by_code_current_code += 2 ;
      if( node->j_input_by_code_current_code > j_code_n1_max )
	node->j_input_by_code_current_code = j_code_n1_min ;
      break ;
      /* C-g による Quit */
    case 0x07 :
      j_keyboard_quit( gw, buffer, node ) ;
      goto exit_menu ;
      /* 画面を表示しなおす。*/
    case 0x0c :
      MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
      j_input_by_code_or_menu_jmp_showCandidate
	( node, node->j_input_by_code_current_code ) ;
      goto exit_menu ;
      /* それ以外のキーは無効。*/
    default :
      err_msg
	( node, "`%s' is not valid here!",
	  charaToString( keybuf->buffer[ keybuf->usage - 1 ] ) ) ;
      goto exit_menu ;
    }
    /* 画面表示。*/
    j_input_by_code_or_menu_jmp_showCandidate
      ( node, node->j_input_by_code_current_code ) ;
  } else {
    /* 候補が選択された場合の処理。*/
    kbuf[ 0 ] = node->j_input_by_code_current_code + ( func_no / 6 ) ;
    kbuf[ 1 ] = j_input_by_code_menu1_codes[ func_no % 6 ] ;
    kbuf[ 2 ] = '\0' ;
    
    node->j_input_by_code_current_code = kbuf[ 0 ] << 8 | kbuf[ 1 ] ;
    node->j_input_by_code_or_menu_mode ++ ;

    /* 一旦ミニバッファへの表示をクリアする。*/
    XtVaSetValues( gw, XtNclearMinibuffer, True, NULL ) ;
    /* きちんと表示する内容を与える。*/
    j_input_by_code_or_menu_1_jmp_showCandidate
      ( node, node->j_input_by_code_current_code ) ;
  }
exit_menu:
  return 0 ;
}

int do_function_j_input_by_code_or_menu_1_jump
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  struct keyBuffer *keybuf ;
  int func_no ;
  unsigned int n1, n2 ;

  /* エラーの表示で消されてしまっているかもしれない候補を再表示する */
  /* しかし、Emacs は自動回復しているみたいだけど…？ */
  j_input_by_code_or_menu_1_jmp_showCandidate
    ( node, node->j_input_by_code_current_code ) ;

  keybuf  = &sbuffer->keybuf ;
  func_no = lookup_at_string
    ( j_input_by_code_menu_keys2, keybuf->buffer[ keybuf->usage - 1 ] ) ;

  if( func_no < 0 ){
  /* 'a','s',…でなかった場合の処理。*/
    n1 = ( node->j_input_by_code_current_code ) >> 8 ;
    n2 = ( node->j_input_by_code_current_code ) & 0x00FF ;
    switch( keybuf->buffer[ keybuf->usage - 1 ] ){
      /* 一つ前の候補へと戻る。*/
    case 'x' :
      n2 -= 15 ;
      if( n2 < j_code_n2_min ){
	n2 = j_code_n2_max - ( j_code_n2_min - n2 ) ;
	n1 -- ;
	if( n1 < j_code_n1_min )
	  n1 = j_code_n1_max ;
      }
      break ;
      /* 次の候補を探す。*/
    case ' ' :
      n2 += 17 ;
      if( n2 > j_code_n2_max ){
	n2 = ( n2 - j_code_n2_max ) + j_code_n2_min ;
	n1 ++ ;
	if( n1 > j_code_n1_max )
	  n1 = j_code_n1_min ;
      }
      break ;
      /* j_keyboard quit の場合の処理…。*/
    case 0x07 :
      j_keyboard_quit( gw, sbuffer, node ) ;
      goto exit_menu_1 ;
      /* 画面を再表示する。*/
    case 0x0c :
      MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
      j_input_by_code_or_menu_1_jmp_showCandidate
	( node, node->j_input_by_code_current_code ) ;
      /* まあ、強制再描画させときます。*/
      /* skkinput_RedrawScreen( gw ) ; */
      goto exit_menu_1 ;
      /* それ以外のキーはエラーになる。*/
    default :
      err_msg
	( node, "`%s' is not valid here!",
	  charaToString( keybuf->buffer[ keybuf->usage - 1 ] ) ) ;
      goto exit_menu_1 ;
    }
    node->j_input_by_code_current_code = ( n1 << 8 ) | ( n2 & 0x00FF ) ;
    j_input_by_code_or_menu_1_jmp_showCandidate
      ( node, node->j_input_by_code_current_code ) ;
  } else {
    /* ここは無茶苦茶怪しい！！！！ */
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 3 * func_no + 2 ] ) ;
    j_insert_myChara
      ( node, node->j_input_by_code_or_menu_point,
	node->mtextbuffer[ 3 * func_no + 1 ] ) ;
    node->j_input_by_code_or_menu_mode = False ;
    MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
  }
exit_menu_1:
  return 0 ;
}

/*
 * 辞書から単語(候補)を削除する関数…上手く動くかどうか謎。
 */
int j_purge_from_jisyo
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  if( node->j_henkan_on && !IS_END_OF_STRING( node->j_henkan_key[ 0 ] ) ){
    /* ミニバッファを利用して、文字列を入力させる。*/
    j_read_string
      ( gw, sbuffer, node, "Really purge? (yes or no) ",
	J_PURGE_YES_OR_NO_P ) ;
    /* 再帰が Widget の中でやったら目茶になったので、もはや使えない…。*/
  }
  return 0 ;
}

/*
 * マークをセットする関数。
 */
int j_set_mark_command( struct SKKInputNode *node )
{
  node->mark_pos = node->cur_pos ;
  err_msg( node, "Mark set" ) ;
  return 0 ;
}

/*
 * 指定された範囲の文字列を抜き出してcutbufferにしまう関数。
 * 消去するかどうかは flag の値による。
 */
static int j_kill_region_sub
( struct SKKInputNode *node, struct myChar *cutbuffer, int flag )
{
  int startpos, cutlen ;

  /* マークがセットされていなかったら駄目。*/
  if( node->mark_pos < 0 ){
    err_msg( node, "The mark is not set now" ) ;
    return 1 ;
  }
  if( node->mark_pos < node->cur_pos ){
    startpos = node->mark_pos ;
    cutlen   = node->cur_pos - node->mark_pos ;
  } else {
    startpos = node->cur_pos ;
    cutlen   = node->mark_pos - node->cur_pos ;
  }
  /* 切り出す意味がなかったら、即抜ける。*/
  if( cutlen <= 0 )
    return 1 ;
  /* きちんと指定された量だけ抜き出す。*/
  myCharStrncpy( cutbuffer, node->textbuffer + startpos, cutlen ) ;
  MYCHAR_SET_END_OF_STRING( cutbuffer[ cutlen ] ) ;

  if( flag )
    /* カーソル以降の文字を消去する。*/
    j_delete_region( node, startpos, startpos + cutlen ) ;
  return 0 ;
}

/*
 * マークとカーソル間の文字列を削除し、その文字列をカットバッファに保
 * 存する関数。
 */
int j_kill_region( struct SKKInputNode *node, struct myChar *cutbuffer )
{
  j_delete_kanaprefix( node ) ;
  return j_kill_region_sub( node, cutbuffer, True ) ;
}

/*
 * カーソルとマーク間の文字列をカットバッファに取り込む関数。削除はし
 * ない。
 */
int j_kill_ring_save( struct SKKInputNode *node, struct myChar *cutbuffer )
{
  j_delete_kanaprefix( node ) ;
  return j_kill_region_sub( node, cutbuffer, False ) ;
}

/*
 * マーク位置とカーソル位置を交換する関数。
 *---
 * マーク位置が正しくない(文字列がそこまで無い)場合には、行末にあった
 * ことにしている。tcsh では無理矢理交換できたりして間に NUL が入るの
 * が見えて楽しい。
 */
int j_exchange_point_and_mark( struct SKKInputNode *node )
{
  int tmp, length ;
  j_delete_kanaprefix( node ) ;
  /* マークが設定されていない場合には交換不可能である。*/
  if( node->mark_pos < 0 ){
    err_msg( node, "No mark set in this buffer" ) ;
    return 1 ;
  }
  /* マーク位置が行末よりも後にあった場合。*/
  length = myCharStrlen( node->textbuffer ) ;
  if( node->mark_pos > length ){
    node->mark_pos = length ;
  }
  tmp = node->cur_pos ;
  node->cur_pos  = node->mark_pos ;
  node->mark_pos = tmp ;
  return 0 ;
}

/*
 * 数値を切り出す関数。
 */
static struct myChar *j_skip_numeric_character
( struct myChar *ptr, struct myChar **j_num_list_top )
{
  struct myChar *j_num_list = *j_num_list_top ;

  /* 文字列が終端されるまで続く。*/
  while( !IS_END_OF_STRING( *ptr ) ){
    switch( ptr->charset ){
    case CHARSET_JISX0208_1978 :
    case CHARSET_JISX0208_1983 :
      /* 漢字コードの１バイト目の処理。*/
      if( ptr->chara < 0x2330 || ptr->chara > 0x2339 )
	goto exit_j_skip_numeric_character ;
      MYCHAR_SET_CHARA( *j_num_list, ptr->chara - 0x2330 + '0' ) ;
      j_num_list ++ ;
      ptr ++ ;
      break ;
    default :
      if( IS_ASCII_CHARA( *ptr ) &&
	  ptr->chara >= '0' && ptr->chara <= '9' ){
	/* 0 から 9 の間だった場合…。*/
	*j_num_list ++ = *ptr ++ ;
	break ;
      }
      goto exit_j_skip_numeric_character ;
    }
  }
exit_j_skip_numeric_character:
  /* どこまで利用したかを入れておく。*/
  *j_num_list_top = j_num_list ;
  return ptr ;
}

static struct myChar *j_identity
( struct myChar *sptr, struct myChar *dptr, int dbufsize )
{
  while( !IS_END_OF_STRING( *sptr ) && !IS_ASCII_EQUAL( *sptr, '/' ) &&
	 dbufsize > 1 ){
    *dptr ++ = *sptr ++ ;
    dbufsize -- ;
  }
  /* 終端文字を入れるためには 1 つは残っていないといけないから。*/
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
  return sptr ;
}

static struct myChar *j_zenkaku_num_str
( struct myChar *sptr, struct myChar *dptr, int dbufsize )
{
  while( !IS_END_OF_STRING( *sptr ) && !IS_ASCII_EQUAL( *sptr, '/' ) &&
	 dbufsize > 1 ){
    dptr->charset = CHARSET_JISX0208_1983 ;
    dptr->chara   = 0x2330 + ( sptr->chara - '0' ) ;
    dptr ++ ;
    sptr ++ ;
    dbufsize -- ;
  }
  /* 終端文字を入れるためには 1 つは残っていないといけないから。*/
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
  return sptr ;
}

static struct myChar *j_kanji_num_str
( struct myChar *sptr, struct myChar *dptr, int dbufsize )
{
  while( !IS_END_OF_STRING( *sptr ) && !IS_ASCII_EQUAL( *sptr, '/' ) &&
	 dbufsize > 1 ){
    *dptr ++ = kanji_num_list[ sptr->chara - '0' ] ;
    sptr ++ ;
    dbufsize -- ;
  }
  /* 終端文字を入れるためには 1 つは残っていないといけないから。*/
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
  return sptr ;
}

static struct myChar *j_kanji_num_str2
( struct myChar *sptr, struct myChar *dptr, int dbufsize )
{
  int len = 0, silent, num ;
  struct myChar *ptr ;

  /* まず長さを判別する。*/
  while( !IS_END_OF_STRING( sptr[ len ] ) &&
	 !IS_ASCII_EQUAL( sptr[ len ], '/' ) )
    len ++ ;
  /* 頭の零は表示されないので無視されるんだけど…。*/
  while( IS_ASCII_EQUAL( *sptr, '0' ) ){
    len -- ;
    sptr ++ ;
  }
  if( len == 0 ){
    if( dbufsize > 1 ){
      *dptr ++ = kanji_num_list[ 0 ] ;
      dbufsize -- ;
    }
  } else {
    num = ( ( len - 1 ) / 4 ) - 1 ;
    /* 大き過ぎる数ですか？ */
    if( num >= 17 ){
      /* こんなでかい数値は無視する。*/
      while( !IS_ASCII_EQUAL( *sptr, '/' ) && !IS_END_OF_STRING( *sptr ) )
	sptr ++ ;
      goto exit_j_kanji_num_str2 ;
    }
    silent = True ;
    while( len > 0 ){
      if( sptr->chara > '1' ||
	  ( !IS_ASCII_EQUAL( *sptr, '0' ) &&
	    ( len % 4 ) == 1 ) ){
	if( dbufsize > 1 ){
	  *dptr ++ = kanji_num_list[ sptr->chara - '0' ] ;
	  dbufsize -- ;
	}
      }
      if( !IS_ASCII_EQUAL( *sptr, '0' ) ){
	/* 十、百、千の位に一つでも〇でないものが入れば、黙っていな *
	 * い…。ここで、黙っていないってのは、億とか万とか表示す   *
	 * るって意味。ちなみに、一の位の時が〇でなかったら、もとも *
	 * と表示するつもりなんだから…ねえ。*/
	if( len % 4 == 2 ){
	  if( dbufsize > 1 ){
	    MYCHAR_SET_JISX0208_1983( *dptr, 0x3d3d /*"十"*/ ) ;
	    dptr ++ ; dbufsize -- ;
	  }
	} else if( len % 4 == 3 ){
	  if( dbufsize > 1 ){
	    MYCHAR_SET_JISX0208_1983( *dptr, 0x4934 /*"百"*/ ) ;
	    dptr ++ ; dbufsize -- ;
	  }
	} else if( len % 4 == 0 ){
	  if( dbufsize > 1 ){
	    MYCHAR_SET_JISX0208_1983( *dptr, 0x4069 /*"千"*/ ) ;
	    dptr ++ ; dbufsize -- ;
	  }
	}
	silent = False ;
      }
      if( !silent || !IS_ASCII_EQUAL( *sptr, '0' ) ){
	num = len - 1 ;
	if( num % 4 == 0 ){
	  num = num / 4 - 1 ;
	  /* 万とか億とか兆とかそういうのを表示する？ */
	  if( num >= 0 ){
	    /* 単位をコピーする。*/
	    ptr = kazu_no_tanni[ num ] ;
	    while( !IS_END_OF_STRING( *ptr ) && dbufsize > 1 ){
	      *dptr ++ = *ptr ++ ;
	      dbufsize -- ;
	    }
	    /* 表示したら黙ることにする。*/
	    silent = True ;
	  }
	}
      }
      len -- ;
      sptr ++ ;
    }
  }
exit_j_kanji_num_str2:
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
  return sptr ;
}

static struct myChar *j_shogi_num_str
( struct myChar *sptr, struct myChar *dptr, int dbufsize )
{
  /* 一文字目は英数字なんだけど…。*/
  if( !IS_ASCII_EQUAL( *sptr, '/' ) && !IS_END_OF_STRING( *sptr ) && 
      dbufsize > 1 ){
    dptr->charset = CHARSET_JISX0208_1983 ;
    dptr->chara   = 0x2330 + ( sptr->chara - '0' ) ;
    dptr ++ ;
    dbufsize -- ;
    MYCHAR_SET_END_OF_STRING( *dptr ) ;
    sptr ++ ;
  } else {
    MYCHAR_SET_END_OF_STRING( *dptr ) ;
    return sptr ;
  }
  /* 二文字目は漢字になる。*/
  if( !IS_ASCII_EQUAL( *sptr, '/' ) && !IS_END_OF_STRING( *sptr ) && 
      dbufsize > 1 ){
    *dptr ++ = kanji_num_list[ sptr->chara - '0' ] ;
    dbufsize -- ;
    MYCHAR_SET_END_OF_STRING( *dptr ) ;
    sptr ++ ;
  } else {
    MYCHAR_SET_END_OF_STRING( *dptr ) ;
    return sptr ;
  }
  /* 区切り文字まで前進する。*/
  while( !IS_ASCII_EQUAL( *sptr, '/' ) && !IS_END_OF_STRING( *sptr ) )
    sptr ++ ;
  return sptr ;
}

/*
 * # を数字に展開する関数。
 *---
 * dbufsize は dptr の容量です。容量オーバを避けるためにあらかじめ渡し
 * ています。これは skkinput が任意長の文字列を編集できないことから来
 * ています。しかし、editor じゃないのだから 
 * (Japanese-Input-Method-Editor とか Windows'95 では言うらしいですけ
 * ど) 任意長の編集が可能な必要はないと思いますけどね。
 */
static struct myChar *j_num_exp
( struct myChar *j_num_list_ptr, int type, struct myChar *dptr,
  int dbufsize )
{
  switch( type ){
  case '0' :
    return j_identity( j_num_list_ptr, dptr, dbufsize ) ;
  case '1' :
    return j_zenkaku_num_str( j_num_list_ptr, dptr, dbufsize ) ;
  case '2' :
    return j_kanji_num_str( j_num_list_ptr, dptr, dbufsize ) ;
  case '3' :
    return j_kanji_num_str2( j_num_list_ptr, dptr, dbufsize ) ;
  case '9' :
    return j_shogi_num_str( j_num_list_ptr, dptr, dbufsize ) ;
  default:
    break ;
  }
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
  return j_num_list_ptr ;
}

/*
 * dbufsize は dptr の容量です。
 */
static int j_numeric_convert
( struct SKKInputNode *node,
  struct myChar *sptr, struct myChar *dptr, int dbufsize )
{
  struct myChar *nptr = node->j_num_list ;

  while( !IS_END_OF_STRING( *sptr ) ){
    if( IS_ASCII_EQUAL( *sptr, '#' ) ){
      sptr ++ ;
      /* # を数字に展開する。*/
      nptr = j_num_exp( nptr, sptr->chara, dptr, dbufsize ) ;
      /* 付け足された文字列の一番最後へと移動する。*/
      while( !IS_END_OF_STRING( *dptr ) ){
	dptr ++ ;
	dbufsize -- ;
      }
      /* 区切り文字を飛ばす。*/
      if( IS_ASCII_EQUAL( *nptr, '/' ) )
	nptr ++ ;
      sptr ++ ;
    } else {
      *dptr ++ = *sptr ++ ;
      dbufsize -- ;
    }
  }
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
  return 0 ;
}

/*
 * 変換される文字列「変換キー」の中の数字を切り出して "#" に置き換える
 * 関数。
 */
static int j_compute_numeric_henkan_key
( struct myChar *dptr, struct myChar *sptr, struct myChar *j_num_list ) 
{
  while( !IS_END_OF_STRING( *sptr ) ){
    /* 漢字コードの何バイト目かを判断する。*/
    switch( sptr->charset ){
    case CHARSET_JISX0208_1978 :
    case CHARSET_JISX0208_1983 :
      if( sptr->chara >= 0x2330 && sptr->chara <= 0x2339 ){
	/* アスキーキャラクタの内の 0 から 9 の間だったら… */
	MYCHAR_SET_CHARA( *dptr, '#' ) ;
	dptr ++ ;
	MYCHAR_SET_CHARA( *j_num_list, sptr->chara - 0x2330 + '\0' ) ;
	j_num_list ++ ;
	/* 数字の続く限りスキップする。*/
	sptr = j_skip_numeric_character( sptr, &j_num_list ) ;
	/* 区切り記号を入れておく。*/
	MYCHAR_SET_CHARA( *j_num_list, '/' ) ;
	j_num_list ++ ;
      } else {
	*dptr ++ = *sptr ++ ;
      }
      break ;
#if 0
    case CHARSET_ASCII :
    case CHARSET_JISX0201_ROMAN :
    case CHARSET_JISX0201_KATAKANA :
    case CHARSET_JISX0212_1990 :
#endif
    default :
      if( IS_ASCII_CHARA( *sptr ) &&
	  sptr->chara >= '0' && sptr->chara <= '9' ){
	/* アスキーキャラクタの内の 0 から 9 の間だったら… */
	MYCHAR_SET_CHARA( *dptr, '#' ) ;
	dptr ++ ;
	sptr = j_skip_numeric_character( sptr, &j_num_list ) ;
	/* 区切り記号を入れておく。*/
	MYCHAR_SET_CHARA( *j_num_list, '/' ) ;
	j_num_list ++ ;
      } else {
	*dptr ++ = *sptr ++ ;
      }
      break ;
    }
  }
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
  MYCHAR_SET_END_OF_STRING( *j_num_list ) ;
  return 0 ;
}

/*
 * 辞書の返り値を解釈して文字列を挿入する関数。
 */
static int j_insert_word
( struct SKKInputNode *node, int pos, struct myChar *word )
{
  struct myChar buffer[ TEXTBUFSIZE ] ;
  /*    struct myChar note[ TEXTBUFSIZE ] ; */
  struct myChar *tmp ;
  int len ;

  if( !IS_END_OF_STRING( node->j_num_list[ 0 ] ) &&
      skkinput_use_numeric_conversion ){
    j_numeric_convert( node, word, buffer, TEXTBUFSIZE ) ;
    word = buffer ;
  }
  len  = myCharStrlen( word ) ;

  /* 文字列を、候補と註釈に分解する。*/
  if (len > 2){
    for( tmp = word ; !IS_END_OF_STRING( *tmp ) ; tmp ++ ){
      if( IS_ASCII_CHARA( *tmp ) && IS_ASCII_EQUAL(*tmp, ';')){
	/*        myCharStrcpy(note, tmp + 1); */
	  MYCHAR_SET_END_OF_STRING(*tmp) ;
	  len  = myCharStrlen( word ) ;
	  break;
      }
    }
  }

  if( IS_ASCII_EQUAL( word[ 0 ], '('  ) &&
      IS_ASCII_EQUAL( word[ len - 1 ], ')' ) ){
    /*
     * うーん。解釈実行ってなわけなんだけどね…。
     */
    if( skkinputlisp_eval( word, buffer ) ){
      word = buffer ;
    }
  }
  if( !node->j_katakana_mode ){
    /* 平仮名と片仮名の交換はしない。*/
    j_insert_str( node, pos, word ) ;
  } else {
    struct myChar katakana_buffer[ TEXTBUFSIZE ] ;
    /* 平仮名と片仮名の交換をする。*/
    len = myCharHiraKataStrnncpy
      ( katakana_buffer, TEXTMAXLEN, word, TEXTMAXLEN ) ;
    MYCHAR_SET_END_OF_STRING( katakana_buffer[ len ] ) ;
    /* 交換された文字列を挿入する。*/
    j_insert_str( node, pos, katakana_buffer ) ;
  }
  return 0 ;
}

/*
 * 与えられた座標から進行方向に一単語移動すると何処まで行くのかを計算
 * する関数。
 * 「ねぇ、どうしてこんな関数が必要なの？ たたりさん」
 * 「ああ。これは upcase-word や downcase-word, capitalize-word にも使
 *   うからだよ」
 * 「ふーん。そうなんだ。ボク、分かったよ！」
 */
static int j_calc_forward_word( struct SKKInputNode *node, int pos )
{
  /* カーソルを移動させる。*/
  if( !MYCHAR_IS_ALPHABETIC( node->textbuffer[ pos ] ) ) {
    /* アルファベットや数字じゃない間はずんずん進む。*/
    while( !IS_END_OF_STRING( node->textbuffer[ pos ] ) &&
	   !MYCHAR_IS_ALPHABETIC( node->textbuffer[ pos ] ) )
      pos ++ ;
  }
  /* もうこれ以上カーソルは移動できないので終了する。*/
  if( IS_END_OF_STRING( node->textbuffer[ pos ] ) )
    return pos ;
  /* アルファベットや数字の間はずんずん進む。*/
  while( !IS_END_OF_STRING( node->textbuffer[ pos ] ) &&
	 MYCHAR_IS_ALPHABETIC( node->textbuffer[ pos ] ) ){
    pos ++ ;
  }
  return pos ;
}

/*
 * 一単語だけカーソルを移動させる関数。
 */
int j_forward_word( struct SKKInputNode *node )
{
  /* 仮名の入力中なら確定にしてしまう。*/
  j_delete_kanaprefix( node ) ;
  /* カーソルを移動させる。*/
  node->cur_pos = j_calc_forward_word( node, node->cur_pos ) ;
  return 0 ;
}

/*
 * 単語を大文字に変換{/小文字に変換/キャピタライズ}する関数。
 */
int j_caseword( struct SKKInputNode *node, int pattern )
{
  int pos ;
  /* 仮名の入力中なら確定にしてしまう。*/
  j_delete_kanaprefix( node ) ;

  /* 現在のカーソル位置を覚えておく。*/
  pos = node->cur_pos ;
  /* 何処までカーソルは移動するのかな？ */
  node->cur_pos = j_calc_forward_word( node, pos ) ;

  /* カーソルが移動できなかったということは、カーソルは行末だから、*
   * 大文字に変換しないといけない文字が何処にもないということになる*/
  if( pos < node->cur_pos ){ 
    if( pattern == 'c' ){
      /* capitilaize の場合。*/
      while( pos < node->cur_pos ){
	/* 一番最初に見付かったアルファベットを大文字に変換して、そ *
	 * れ以降は小文字に変換する。*/
	if( MYCHAR_IS_LOWERCASE( node->textbuffer[ pos ] ) ){
	  node->textbuffer[ pos ].chara += ( 'A' - 'a' ) ;
	  pos ++ ;
	  break ;
	}
	/* 一番最初に見付かったアルファベットが大文字なら変換は不要 */
	if( MYCHAR_IS_UPPERCASE( node->textbuffer[ pos ] ) ){
	  pos ++ ;
	  break ;
	}
	pos ++ ;
      }
      /* 後は downcase の場合と同じ処理になる。*/
      pattern = 'd' ;
    }
    while( pos < node->cur_pos ){ 
      /* 大文字に変換する。*/
      if( MYCHAR_IS_LOWERCASE( node->textbuffer[ pos ] ) &&
	  pattern == 'u' ){
	node->textbuffer[ pos ].chara += ( 'A' - 'a' ) ;
      } else if( MYCHAR_IS_UPPERCASE( node->textbuffer[ pos ] ) &&
		 pattern == 'd' ){
	node->textbuffer[ pos ].chara += ( 'a' - 'A' ) ;
      }
      pos ++ ;
    }
  }
  return 0 ;
}

/*
 * 一単語だけカーソルを移動させる関数。
 */
int j_backward_word( struct SKKInputNode *node )
{
  /* 仮名の入力中なら確定にしてしまう。*/
  j_delete_kanaprefix( node ) ;
  /* カーソル位置は正しいか？ */
  if( node->cur_pos <= node->cur_pos_top )
    return 1 ;
  /* 自分が現在見ている場所は見ない。*/
  node->cur_pos -- ;
  /* カーソルを移動させる。*/
  if( !MYCHAR_IS_ALPHABETIC( node->textbuffer[ node->cur_pos ] ) ) {
    /* アルファベットや数字じゃない間はずんずん進む。*/
    while( node->cur_pos > node->cur_pos_top &&
	   !MYCHAR_IS_ALPHABETIC( node->textbuffer[ node->cur_pos ] ) ){
      /* 一文字前に移動する。*/
      node->cur_pos -- ;
    }
  }
  /* もうこれ以上カーソルは移動できないので終了する。*/
  if( node->cur_pos == node->cur_pos_top )
    return 0 ;
  /* アルファベットや数字の間はずんずん進む。*/
  while( node->cur_pos >= node->cur_pos_top &&
	 MYCHAR_IS_ALPHABETIC( node->textbuffer[ node->cur_pos ] ) ){
    /* 一文字前に移動する。*/
    node->cur_pos -- ;
  }
  node->cur_pos ++ ;
  return 0 ;
}

/*
 * そのまま辞書登録されてしまうとまずい人達を変換する関数。
 *----
 * のサブだけど。
 */
static int j_quote_char_sub( struct myChar *word )
{
  struct myChar *dptr, *sptr ;
  int bufsize ;
  /* そのまま存在しちゃまずい人達を列挙している。*/
  struct chara_string_pair conv_pattern[] = {
    { '\r', "\\r"   }, { '\n', "\\n"   }, { '/',  "\\057" },
    { '\\', "\\\\"  }, { '[',  "\["    }, { '\"', "\\\""  },
    { '\0', NULL    },
  } ;
  struct chara_string_pair *cptr ;

  /* まずはおまじないを用意する。*/
  myCharCharStrcpy( temporary_buffer, "(concat \"" ) ;
  bufsize = myCharStrlen( temporary_buffer ) ;
  dptr = temporary_buffer + bufsize ;
  sptr = word ;
  bufsize = TEXTMAXLEN - bufsize - 2 ;

  /* 変換ループ。*/
  while( !IS_END_OF_STRING( *sptr ) && bufsize > 0 ){
    /* ASCII 文字列の場合には不味いことが生じる可能性がある。*/
    if( IS_ASCII_CHARA( *sptr ) ){
      /* 不味いものリストをながめる。*/
      cptr = conv_pattern ;
      while( cptr->chara != '\0' ){
	if( sptr->chara == cptr->chara ){
	  int tmplen = strlen( cptr->string ) ;
	  if( bufsize < tmplen )
	    goto exit_j_quote_sub ;
	  myCharCharStrcpy( dptr, cptr->string ) ;
	  dptr += tmplen ;
	  bufsize -= tmplen ;
	  sptr ++ ;
	  break ;
	}
	cptr ++ ;
      }
      if( cptr->chara != '\0' )
	continue ;
    }
    /* ええと、何にもヒットしなかった。つまり修正不要だった場合。*/
    *dptr ++ = *sptr ++ ;
    bufsize -- ;
  }
 exit_j_quote_sub:
  /* 最後閉じておくための処理。この文字を入れるために -2 なんてのが付 *
   * いていたわけだ。*/
  MYCHAR_SET_CHARA( *dptr, '\"' ) ;
  dptr ++ ;
  MYCHAR_SET_CHARA( *dptr, ')' ) ;
  dptr ++ ;
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
  /* 最後に修正後の文字列を返す。*/
  myCharStrcpy( word, temporary_buffer ) ;
  return True ;
}

/*
 * そのまま辞書登録されてしまうとまずい人達を変換する関数。
 *----
 * (注意) temporary_buffer を利用する。
 */
static int j_quote_char( struct myChar *word )
{
  struct myChar *ptr, *tmp ;
  int len;
  /* ええと、lisp の命令だった場合には修正しない。でも、skkinput が解 *
   * 釈できる lisp の命令って少ないから…。*/
  len = myCharStrlen( word ) ;
  if( len > 2 && IS_ASCII_EQUAL( word[ 0 ], '(' ) &&
      MYCHAR_IS_ALPHABETIC( word[ 1 ] ) &&
      IS_ASCII_EQUAL( word[ len - 1 ], ')' ) ){
    return False ;
  }
  /* 註釈 付きのときも修正しない。*/
  if (len > 2){
    for( tmp = word ; !IS_END_OF_STRING( *tmp ) ; tmp ++ ){
      if( IS_ASCII_CHARA( *tmp ) && IS_ASCII_EQUAL(*tmp, ';'))
	return False;
      }
  }

  /* 内部に修正しないといけない文字が入っているかどうかチェックする。*/
  for( ptr = word ; !IS_END_OF_STRING( *ptr ) ; ptr ++ ){
    if( IS_ASCII_CHARA( *ptr ) ){
      switch( ptr->chara ){
      case '/' :
      case '\n' :
      case '\r' :
      case '\\' :
      case '[' :
      case ']' :
	return j_quote_char_sub( word ) ;
      default :
	break ;
      }
    }
  }
  return False ;
}

/*
 * 註釈でないセミコロンを変換する関数。
 */
static int j_quote_semicolon( struct myChar *word )
{
  struct myChar *dptr, *sptr ;
  int bufsize ;
  /* そのまま存在しちゃまずい人達を列挙している。*/
  struct chara_string_pair conv_pattern[] = {
    { ';',  "\\073" },
    { '\r', "\\r"   }, { '\n', "\\n"   }, { '/',  "\\057" },
    { '\\', "\\\\"  }, { '[',  "\["    }, { '\"', "\\\""  },
    { '\0', NULL    },
  } ;
  struct chara_string_pair *cptr ;

  /* まずはおまじないを用意する。*/
  myCharCharStrcpy( temporary_buffer, "(concat \"" ) ;
  bufsize = myCharStrlen( temporary_buffer ) ;
  dptr = temporary_buffer + bufsize ;
  sptr = word ;
  bufsize = TEXTMAXLEN - bufsize - 2 ;

  /* 変換ループ。*/
  while( !IS_END_OF_STRING( *sptr ) && bufsize > 0 ){
    /* ASCII 文字列の場合には不味いことが生じる可能性がある。*/
    if( IS_ASCII_CHARA( *sptr ) ){
      /* 不味いものリストをながめる。*/
      cptr = conv_pattern ;
      while( cptr->chara != '\0' ){
	if( sptr->chara == cptr->chara ){
	  int tmplen = strlen( cptr->string ) ;
	  if( bufsize < tmplen )
	    goto exit_j_quote_sub ;
	  myCharCharStrcpy( dptr, cptr->string ) ;
	  dptr += tmplen ;
	  bufsize -= tmplen ;
	  sptr ++ ;
	  break ;
	}
	cptr ++ ;
      }
      if( cptr->chara != '\0' )
	continue ;
    }
    /* ええと、何にもヒットしなかった。つまり修正不要だった場合。*/
    *dptr ++ = *sptr ++ ;
    bufsize -- ;
  }
 exit_j_quote_sub:
  /* 最後閉じておくための処理。この文字を入れるために -2 なんてのが付 *
   * いていたわけだ。*/
  MYCHAR_SET_CHARA( *dptr, '\"' ) ;
  dptr ++ ;
  MYCHAR_SET_CHARA( *dptr, ')' ) ;
  dptr ++ ;
  MYCHAR_SET_END_OF_STRING( *dptr ) ;
  /* 最後に修正後の文字列を返す。*/
  myCharStrcpy( word, temporary_buffer ) ;
  return True ;
}

/*
 * リングバッファ構造に文字列を格納する関数。
 *----
 * バグがある？
 */
static int save_ringbuffer
( struct myChar *buffer, int bufsize, int *startpos, int *endpos,
  struct myChar *string )
{
  int spos, overrightflag ;
  struct myChar *ptr ;

  /* 空白文字列だったり、先頭改行コードだったりした場合には登録しない。*/
  if( IS_ASCII_EQUAL( string[ 0 ], '\n' ) ||
      IS_ASCII_EQUAL( string[ 0 ], '\r' ) ||
      IS_END_OF_STRING( string[ 0 ] ) )
    return 0 ;
  /* ここから ring buffer への save */
  spos          = *endpos ;
  ptr           = buffer + spos ;
  overrightflag = False ;
  do {
    /* 文字列を ring buffer へとする。*/
    *ptr ++ = *string ++ ;
    spos ++ ;
    /* バッファの最後まで到達してしまったら先頭に戻す。*/
    if( spos >= bufsize ){
      spos = 0 ;
      ptr  = buffer ;
    }
    if( spos == *startpos )
      overrightflag = True ;
  } while( !IS_END_OF_STRING( *string ) ) ;

  /* NUL の次へと送る。*/
  spos ++ ;
  MYCHAR_SET_END_OF_STRING( *ptr ) ;
  ptr ++ ;
  if( spos >= bufsize ){
    spos = 0 ;
    ptr  = buffer ;
  }

  /* 以前に登録したあった候補を上書きしてしまった？ */
  *endpos = spos ;
  if( overrightflag ){
    /* 取り敢えず、現在の位置は次の候補が登録される場所になる。*/
    /* 次の候補の開始位置までスキップする。*/
    do {
      MYCHAR_SET_END_OF_STRING( *ptr ) ;
      ptr ++ ;
      spos ++ ;
      /* バッファの最後まで到達してしまったら先頭に戻す。*/
      if( spos >= bufsize ){
	spos = 0 ;
	ptr  = buffer ;
      }
    } while( !IS_END_OF_STRING( *ptr ) ) ;
    /* '\0' の次からがそう。*/
    *startpos = spos ;
  }
  return 0 ;
}

/*
 * コンプリーションを終了させる関数。
 *---------
 * コンプリーションを最初に行った時に得たリストを解放する処理を行って
 * いる。
 */
void j_completion_close
( struct SKKInputNode *node )
{
  /* 前回利用されたかもしれないコンプリーションの情報は捨てる。*/
  free_VectorIndex( &node->j_completion_vector_index_top ) ;
  free_SkkinpSearchVector( &node->j_completion_vector_top ) ;

  node->j_completion_vector_top           = NULL ;
  node->j_completion_vector_index_top     = NULL ;
  node->j_current_completion_vector_index = NULL ;
  node->j_completion_mode                 = False ;
  return ;
}

/*
 * 辞書の変更(更新)フラグを消す関数。
 *----
 * これを実行することにより、オートセーブも行われなくなる。
 */
void j_clear_modified_flag( void )
{
  skkinput_jisyo_dirty          = False ;
  skkinput_autosave_jisyo_dirty = False ;
  return ;
}

/*
 * キー入力を変換クライアントに送り返す関数。
 */
void j_sendback_keypress( Widget gw, XEvent *xevent )
{
  Window to_window, subwindow ;

  /* うーむ？ 何故か窓を覚えておく…。*/
  to_window = xevent->xkey.window ;
  subwindow = xevent->xkey.subwindow ;
  /* イベントをくっつけて渡す。*/
  XtCallCallbacks( gw, XtNkeybackNotify, xevent ) ;
  /* 元に戻している。*/
  xevent->xkey.window    = to_window ;
  xevent->xkey.subwindow = subwindow ;
  return ;
}

void j_mode_off_and_self_insert
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, int chara )
{
  j_mode_off( gw, sbuffer, node, chara ) ;
  j_self_insert( gw, sbuffer, node, chara ) ;
  return ;
}

/*
 * "oh" のチェックを行うのに利用される関数。
 */
void j_oh_check_with_controlcode
( struct SKKInputNode *node, int keyboard_quit_p )
{
  struct myChar lchara ;

  /* 仮名入力の途中でなければ意味がない。*/
  if( !node->j_kana_mode )
    return ;

  if( !IS_ASCII_EQUAL( node->j_prefix[ 0 ], 'o' ) )
    return ;

  /* 先に入力されたのが "o" だった場合の処理。*/
  if( IS_END_OF_STRING( node->j_prefix[ 1 ] ) ){
    MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
    node->j_kana_mode = False ;
    node->j_kana_start_point = -1 ;
    return ;
  }
  /* oh の入力の途中でなければ全然関係ないので無視する。*/
  if( !IS_ASCII_EQUAL( node->j_prefix[ 1 ], 'h' ) ||
      !IS_END_OF_STRING( node->j_prefix[ 2 ] ) ||
      keyboard_quit_p )
    return ;

  /* プレフィクス文字列を抹消する。*/
  MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;
  /* 仮名の開始位置が "h" でなければおかしい。*/
  if( IS_ASCII_EQUAL
      ( node->textbuffer[ node->j_kana_start_point ], 'h' ) ){
    delete_char( node, node->j_kana_start_point, 1 ) ;
    /* "お" を挿入する。*/
    if( node->j_katakana_mode ){
      MYCHAR_SET_JISX0208_1983( lchara, 0x252a /*"オ"*/ ) ;
    } else {
      MYCHAR_SET_JISX0208_1983( lchara, 0x242a /*"お"*/ ) ;
    }
    j_insert_myChara( node, node->j_kana_start_point, lchara ) ;
  }
  /* "h" の入力の途中であるという「仮名モード」は停止する。*/
  node->j_kana_mode = False ;
  return ;
}

/*
 * ＃………………こんなところを読んでいる人もいるとは………。
 * 遅くなりましたけど、「誕生日おめでとう、若葉」
 * 
 * http://www2m.meshnet.or.jp/~maina/part3/wakaba/birth0.htm
 * ＃詳細は、上記の url を参照のこと。(^^) ちょっとどころじゃなく重い
 * けど。
 */
