/* # skkinput (Simple Kana-Kanji Input)
 * skkconfig.c --- Read dot.skkinput.
 * 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 <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>

#include "commondef.h"
#include "buffers.h"
#include "SeparateWin.h"
#include "config.h"
#include "skkkey.h"

/*
 * 定数定義、構造体の宣言…。
 */
enum {
  PARAM_BOOLEAN,
  PARAM_INTEGER,
  PARAM_STRING,
  PARAM_PATH,
  PARAM_INPUT_VECTOR,
  PARAM_ROM_KANA_RULE_LIST, 
#ifdef THIS_IS_NECESSARY
  PARAM_DEFINE_KEY_SKKMAP,
#endif
  PARAM_DEFINE_KEY_SKKJMAP,
  PARAM_DEFINE_KEY_ABBREVMAP,
  PARAM_SKKINPUTLISP,
#ifdef THIS_IS_NECESSARY
  PARAM_UNDEFINE_KEY_SKKMAP,
#endif
  PARAM_UNDEFINE_KEY_SKKJMAP,
  PARAM_UNDEFINE_KEY_ABBREVMAP,
  PARAM_END,
} ;

typedef struct {
  char *string ;
  int action ;
  void *argument ;
} SkkInpDef ;

struct skk_rom_kana_rule_list {
  struct skk_rom_kana_rule element ;
  struct skk_rom_kana_rule_list *next ;
} ;

/*
 * プロトタイプ宣言。
 */
/* skkldic.c */
extern int check_skkinput_jisyo_code( char *path ) ;
/* parseStr.c */
struct myChar *skip_empty_symbols( struct myChar *ptr ) ;
struct myChar *parseString
( struct myChar *string, struct myChar **dest, int *result ) ;
unsigned char *expand_file_name( unsigned char *path ) ;
int parseKeyString
( struct myChar *string, struct myChar *buffer, int bufsize ) ;

/* lispeval.c */
extern int skkinputlisp_weak_eval
( struct myChar *commandstring ) ;

/* keymap.c */
extern int skkinput_CompileDefaultKeyBind( void ) ;
extern int skkinput_DefineKey
( struct myChar *string, int length, int func_no, int map ) ;
extern int skkinput_UndefineKey
( struct myChar *string, int length, int map ) ;

/*
 * skkinput の動作を決定する変数群。
 */
static int  skkserv_portnum ;	/* obsoleted, use skkserv_service */
extern char *skkserv_host ;
extern char *skkserv_service;
extern int skkserv_pf;
static char *skkserv_protocol_family ;
extern char *skkinput_local_jisyo_name ;
extern char *skkinput_backup_jisyo_name ;
extern char *skk_local_jisyo_name ;
extern char *skkinput_record_name ;
extern int  skk_egg_like_newline ;
extern int  skkinput_dabbrev_like_completion ;
extern int  skkinput_chatadaptermode ;
extern int  skkinput_search_skk_jisyo ;
extern int  skkinput_keep_record ;
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 int  skkinput_rjj_like_input ;

extern Boolean skkinput_jisyo_dirty ;

extern unsigned long skkinput_autosave_interval ;

/*
 * 初期入力ベクトル。
 */
static unsigned char *skkinput_default_input_vector[ 128 ] = {
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  "！",  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
  NULL,  NULL,  NULL,  NULL,  "、",  "ー",  "。",  NULL,
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  NULL,  "：",  "；",  NULL,  NULL,  NULL,  "？",
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  NULL,  NULL,  "「",  NULL,  "」",  NULL,  NULL,
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
} ;

/*
 * 実際に変換に使われる入力ベクトル。
 */
struct myChar *skkinput_input_vector[ 128 ] ;

/*
 * 初期全角入力ベクトル。
 */
static unsigned char *skkinput_default_zenkaku_vector[ 128 ] = {
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL, 
  "　",  "！",  "”",  "＃",  "＄",  "％",  "＆",  "’",
  "（",  "）",  "＊",  "＋",  "，",  "−",  "．",  "／",
  "０",  "１",  "２",  "３",  "４",  "５",  "６",  "７", 
  "８",  "９",  "：",  "；",  "＜",  "＝",  "＞",  "？",
  "＠",  "Ａ",  "Ｂ",  "Ｃ",  "Ｄ",  "Ｅ",  "Ｆ",  "Ｇ",
  "Ｈ",  "Ｉ",  "Ｊ",  "Ｋ",  "Ｌ",  "Ｍ",  "Ｎ",  "Ｏ", 
  "Ｐ",  "Ｑ",  "Ｒ",  "Ｓ",  "Ｔ",  "Ｕ",  "Ｖ",  "Ｗ",  
  "Ｘ",  "Ｙ",  "Ｚ",  "［",  "＼",  "］",  "＾",  "＿",  
  "‘",  "ａ",  "ｂ",  "ｃ",  "ｄ",  "ｅ",  "ｆ",  "ｇ",  
  "ｈ",  "ｉ",  "ｊ",  "ｋ",  "ｌ",  "ｍ",  "ｎ",  "ｏ",  
  "ｐ",  "ｑ",  "ｒ",  "ｓ",  "ｔ",  "ｕ",  "ｖ",  "ｗ",  
  "ｘ",  "ｙ",  "ｚ",  "｛",  "｜",  "｝",  "〜",  NULL,
} ;

/*
 * 実際に変換に使われる全角ベクトル。
 */
struct myChar *skkinput_zenkaku_vector[ 128 ] ;

/*
 * 初期ローマ字仮名変換ルールリスト。
 */
static struct skk_rom_kana_rule 
skkinput_default_rom_kana_rule_list[] = {
  { NULL, NULL, NULL, NULL },
  { NULL, NULL, NULL, NULL },
  { NULL, NULL, NULL, NULL },
} ;

static struct skk_rom_kana_rule_list *skkinput_rom_kana_rule_list_top ;

extern struct skk_rom_kana_rule *skkinput_rom_kana_rule_list ;

/*
 * .skkinput に記述できる操作の一覧。
 */
SkkInpDef skkinput_initdef[] = {
  { "skk-port-num:",		
    PARAM_INTEGER,		&skkserv_portnum },
  { "skk-service:",
    PARAM_STRING,		&skkserv_service },
  { "skk-server-protocol-family:",
    PARAM_STRING,		&skkserv_protocol_family },
  { "skk-server-host:",	
    PARAM_STRING,		&skkserv_host },
  { "skkinput-jisyo:",
    PARAM_PATH,		 	&skkinput_local_jisyo_name },
  { "skkinput-backup-jisyo:",
    PARAM_PATH,		 	&skkinput_backup_jisyo_name },
  { "skk-jisyo:",
    PARAM_PATH,		 	&skk_local_jisyo_name },
  { "skkinput-record:",
    PARAM_PATH,		 	&skkinput_record_name },
  { "skkinput-egg-like-newline:",
    PARAM_BOOLEAN,		&skk_egg_like_newline },
  { "skkinput-dabbrev-like-completion:",
    PARAM_BOOLEAN,		&skkinput_dabbrev_like_completion },
  { "skkinput-chat-adapter-mode:",
    PARAM_BOOLEAN,		&skkinput_chatadaptermode },
  { "skkinput-keep-record:",
    PARAM_BOOLEAN,		&skkinput_keep_record },
  { "skkinput-search-skk-jisyo:",
    PARAM_BOOLEAN,		&skkinput_search_skk_jisyo },
  { "skkinput-date-ad:",
    PARAM_BOOLEAN,		&skkinput_date_ad },
  { "skkinput-number-style:",
    PARAM_INTEGER,		&skkinput_number_style },
  { "skkinput-delete-implies-kakutei:",
    PARAM_BOOLEAN,		&skkinput_delete_implies_kakutei },
  { "skkinput-use-numeric-conversion:",
    PARAM_BOOLEAN,		&skkinput_use_numeric_conversion },
  { "skkinput-zenkaku-vector:",
    PARAM_INPUT_VECTOR,		skkinput_zenkaku_vector },
  { "skkinput-input-vector:",
    PARAM_INPUT_VECTOR,		skkinput_input_vector },
  { "skkinput-rom-kana-rule-list:",
    PARAM_ROM_KANA_RULE_LIST,	&skkinput_rom_kana_rule_list_top },
#ifdef THIS_IS_NECESSARY
  { "define-key-skkmap:",
    PARAM_DEFINE_KEY_SKKMAP,	NULL },
  { "define-key-skkjmap:",
    PARAM_DEFINE_KEY_SKKJMAP,	NULL },
#else
  { "define-key-skkmap:",
    PARAM_DEFINE_KEY_SKKJMAP,	NULL },
#endif
  { "define-key-abbrevmap:",
    PARAM_DEFINE_KEY_ABBREVMAP,	NULL },
#ifdef THIS_IS_NECESSARY
  { "undefine-key-skkmap:",
    PARAM_UNDEFINE_KEY_SKKMAP,	NULL },
  { "undefine-key-skkjmap:",
    PARAM_UNDEFINE_KEY_SKKJMAP,	NULL },
#else
  { "undefine-key-skkmap:",
    PARAM_UNDEFINE_KEY_SKKJMAP,	NULL },
#endif
  { "undefine-key-abbrevmap:",
    PARAM_UNDEFINE_KEY_ABBREVMAP,NULL },
  { "tab-width:",
    PARAM_INTEGER,		&skkinput_tab_width },
  { "skkinput-lisp:",
    PARAM_SKKINPUTLISP,	 	NULL },
  { "skkinput-rjj-like-input:",
    PARAM_BOOLEAN,		&skkinput_rjj_like_input },
  { "skkinput-autosave-interval:",
    PARAM_INTEGER,		&skkinput_autosave_interval },
  { NULL,
    PARAM_END,		NULL },
} ;

/*
 * 指定された文字列が空白文字だけからなるのかどうかを返す関数。
 *----
 * コメント以下は空白文字であるとして扱う。
 */
static int is_empty_line( struct myChar *ptr )
{
  while( !IS_END_OF_STRING( *ptr ) &&
	 !IS_ASCII_EQUAL( *ptr, ';' ) ){
    if( !IS_ASCII_EQUAL( *ptr, 0x20 ) &&
	!IS_ASCII_EQUAL( *ptr, '\t' ) &&
	!IS_ASCII_EQUAL( *ptr, '\n' ) &&
	!IS_ASCII_EQUAL( *ptr, '\r' ) )
      return False ;
    ptr ++ ;
  }
  return True ;
}

/*
 * Boolean つまり "t" か "nil" のどちらが設定されているか見る関数。
 */
static int skkinput_analyze_boolean
( struct myChar *oneline, void *argument )
{
  oneline = skip_empty_symbols( oneline ) ;

  /* 真とすべく設定されているかどうかを判断する。*/
  if( IS_ASCII_EQUAL( *oneline, 't' ) && is_empty_line( oneline + 1 ) ){
    *( int *)argument = True ;
    return True ;
  } 
  /* 偽とすべく設定されているかどうかを判断する。*/
  if( !myCharCharStrncmp( oneline, "nil", 3 ) ){
    if( is_empty_line( oneline + 3 ) ){
      *( int *)argument = False ;
      return True ;
    }
  }
  return False ;
}

static struct myChar *skip_number_digit( struct myChar *ptr )
{
  /* 数字が続く限りスキップする。*/
  while( !IS_END_OF_STRING( *ptr ) ){
    if( !IS_ASCII_CHARA( *ptr ) || ptr->chara < '0' || ptr->chara > '9' )
      break ;
    ptr ++ ;
  }
  return ptr ;
}

static struct myChar *analyze_integer_sub
( struct myChar *ptr, int *result, int *value )
{
  struct myChar *start ;
  struct myChar ch ;
  /* まず空の文字をスキップして、と。*/
  start = skip_empty_symbols( ptr ) ;
  /* それから、文字列と覚しき場所を抜き出します。*/
  ptr = skip_number_digit( start ) ;
  /* 文字がなかったら、と。*/
  if( start == ptr ){
    *result = False ;
    return ptr ;
  }
  /* 数字がきちんと入っていたら、と。*/
  ch     = *ptr ;
  MYCHAR_SET_END_OF_STRING( *ptr ) ;
  *value = myCharAtoi( start ) ;
  *ptr   = ch ;
  *result = True ;
  return ptr ;
}

/*
 * なんて整数が設定されているか見る関数。
 */
static int skkinput_analyze_integer
( struct myChar *oneline, void *argument )
{
  int result, value ;
  oneline = analyze_integer_sub( oneline, &result, &value ) ;
  if( !result )
    return False ;
  /* 数値の次が空白ではない場合にはエラー。*/
  if( !is_empty_line( oneline ) ){
    /* 値の設定はしない。*/
    return False ;
  }
  *( int *)argument = value ;
  return True ;
}

/*
 * なんて文字列が設定されているか見る関数。
 */
static int skkinput_analyze_string
( struct myChar *oneline, void *argument,
  int expandFilenameFlag, int convertBackslash )
{
  struct myChar *result ;
  unsigned char *asciiString ;
  int ret ;

  oneline = parseString( oneline, &result, &ret ) ;
  /* エラーの判定。*/
  if( ret < 0 )
    return False ;
  if( !is_empty_line( oneline ) ){
    /* 後ろにゴミが付いているぜ、それじゃ駄目だぜ、ベイビー〜！ */
    free( result ) ;
    return False ;
  }
  /* 文字列をコピーだぜ！ */
  asciiString = myCharStringToAsciiString( result ) ;
  free( result ) ;
  if( expandFilenameFlag ){
    *( unsigned char **)argument = expand_file_name( asciiString ) ;
    free( asciiString ) ;
  } else {
    *( unsigned char **)argument = asciiString ;
  }
  return True ;
}

/*
 * 与えられた一行は入力ベクトルの設定であるから、それを解釈して、
 * skkinput_input_vector や skkinput_zenkaku_vector に代入しようと努力
 * する関数。
 */
static int skkinput_analyze_input_vector
( struct myChar *oneline, void *argument )
{
  struct myChar *ptr ;
  struct myChar *istring ;
  int result, value, ret ;

  istring = NULL ;
  /* 最初の空白文字をスキップします。*/
  oneline = skip_empty_symbols( oneline ) ;
  /* ( 123  "" ) */ 
  if( !IS_ASCII_EQUAL( *oneline, '(' ) )
    goto err_ret ;
  oneline ++ ;
  /* 数値を抜き出します。*/
  ptr = analyze_integer_sub( oneline, &result, &value ) ;
  /* 結果が返ってこなかったら、駄目でしょ。*/
  if( !result )
    goto err_ret ;
  /* input vector に相応しくない値だったら、捨てましょう。*/
  if( value > 128 || value < 0 )
    goto err_ret ;
  /* そのまま最後まで行ってしまったら、駄目でしょう。*/
  ptr = skip_empty_symbols( ptr ) ;
  if( !IS_ASCII_EQUAL( *ptr, '.' ) )
    goto err_ret ;
  /* 次に文字列を解析します。*/
  ptr = parseString( ptr + 1, &istring, &ret ) ;
  if( ret < 0 )
    goto err_ret ;
  /* 次に続く空白文字列を飛ばします。*/
  ptr = skip_empty_symbols( ptr ) ;
  /* 最後に ")" で閉じられていなければ、駄目です。*/
  if( !IS_ASCII_EQUAL( *ptr, ')' ) )
    goto err_ret ;
  ptr ++ ;
  /* その後にゴミがあってはいけません。*/
  if( !is_empty_line( ptr ) )
    goto err_ret ;
  /* input vector を更新します。*/
  if( ( ( struct myChar ** )argument )[ value ] != NULL ){
    free( ( ( struct myChar ** )argument )[ value ] ) ;
  }
  ( ( struct myChar ** )argument )[ value ] = istring ;
  return True ;
  
err_ret:
  if( istring != NULL )
    free( istring ) ;
  return False ;
}

static void add_skkinput_rule_list
( struct skk_rom_kana_rule_list **top,
  struct skk_rom_kana_rule element )
{
  struct skk_rom_kana_rule_list *node ;
  if( *top == NULL ){
    *top = node = malloc( sizeof( struct skk_rom_kana_rule_list ) ) ;
  } else {
    node = *top ;
    while( node->next != NULL )
      node = node->next ;
    node->next = malloc( sizeof( struct skk_rom_kana_rule_list ) ) ;
    node = node->next ;
  }
  if( node == NULL ){
    fprintf( stderr, "Fatal: Memory Exhausted.\n" ) ;
    exit( 1 ) ;
  }
  node->next = NULL ;
  /* 構造体をコピーする。*/
  node->element = element ;
  return ;
}

static int skkinput_analyze_rule_list
( struct myChar *oneline, void *argument )
{
  struct myChar *ptr, *rstr[ 4 ] ;
  struct skk_rom_kana_rule node ;
  int i, ret ;

  /* 最初の空白文字をスキップします。*/
  oneline = skip_empty_symbols( oneline ) ;
  /* ( "nn" "" "ん" "ン" ) */ 
  if( !IS_ASCII_EQUAL( *oneline, '(' ) )
    return False ;
  oneline ++ ;
  ptr = oneline ;
  for( i = 0 ; i < 4 ; i ++ ){
    /* 次に文字列を解析します。*/
    ptr = parseString( ptr, rstr + i, &ret ) ;
    if( ret < 0 ){
      while( i < 4 )
	rstr[ i ++ ] = NULL ;
      goto err_analyze ;
    }
  }
  /* 文字列が終わったら、")" で閉じている筈。*/
  ptr = skip_empty_symbols( ptr ) ;
  if( !IS_ASCII_EQUAL( *ptr, ')' ) )
    goto err_analyze ;
  /* で、")" の続きは空白文字だけの筈(もしくはコメントだけどね) */
  ptr ++ ;
  if( !is_empty_line( ptr ) )
    goto err_analyze ;
  /* で、抜き出しまして、と。*/
  node.state   = rstr[ 0 ] ;
  node.next    = rstr[ 1 ] ;
  node.houtput = rstr[ 2 ] ;
  node.koutput = rstr[ 3 ] ;
  /* 現在の状態が無かったら、そりゃエラーっす。出力の方もチェックします。*/
  if( node.state == NULL )
    goto err_analyze ;
  add_skkinput_rule_list( argument, node ) ;
  return True ;
err_analyze:
  for( i = 0 ; i < 4 ; i ++ ){
    if( rstr[ i ] != NULL )
      free( rstr[ i ] ) ;
  }
  return False ;
}

/* ( "@" . "j-self-insert" ) */
static int skkinput_analyze_define_key
( struct myChar *oneline,
  struct myChar **rkeystring,
  struct myChar **rkeyfunction,
  int *rlength )
{
  struct myChar *keyfunction, *ptr, *keystring ;
  int result ;

  keystring = keyfunction = NULL ;

  /* 最初の空白文字をスキップします。*/
  oneline = skip_empty_symbols( oneline ) ;
  /* ( "nn" "" "ん" "ン" ) */ 
  if( !IS_ASCII_EQUAL( *oneline, '(' ) )
    goto err_ret ;
  oneline ++ ;
  /* 次に文字列を解析します。*/
  ptr = parseString( oneline, &keystring, &result ) ;
  /* 失敗した場合…。*/
  if( keystring == NULL || result < 0 )
    goto err_ret ;
  *rlength = result ;
  /* そのまま最後まで行ってしまったら、駄目でしょう。*/
  ptr = skip_empty_symbols( ptr ) ;
  if( !IS_ASCII_EQUAL( *ptr, '.' ) )
    goto err_ret ;
  /* 次に文字列を解析します。*/
  ptr = parseString( ptr + 1, &keyfunction, &result ) ;
  /* 失敗した場合…。*/
  if( keyfunction == NULL || result < 0 )
    goto err_ret ;
  /* 次に続く空白文字列を飛ばします。*/
  ptr = skip_empty_symbols( ptr ) ;
  /* 最後に ")" で閉じられていなければ、駄目です。*/
  if( !IS_ASCII_EQUAL( *ptr, ')' ) )
    goto err_ret ;
  ptr ++ ;
  /* その後にゴミがあってはいけません。*/
  if( !is_empty_line( ptr ) )
    goto err_ret ;
  /* 結果を代入します。*/
  *rkeystring   = keystring ;
  *rkeyfunction = keyfunction ;
  return True ;
err_ret:
  if( keystring != NULL )
    free( keystring ) ;
  if( keyfunction != NULL )
    free( keyfunction ) ;
  return False ;
}

/*
 * 関数名を関数番号に変換する処理を行う関数。
 */
static int skkinput_string2function_number
( struct myChar *keyfunction )
{
  int func_no ;
  static unsigned char *function_string_table[] = {
    "self-insert-command",
    "j-self-insert",
    "j-self-zenkaku-insert",
    "j-display-code-for-char-at-point",
    "j-set-henkan-point",
    "j-set-henkan-point-subr",
    "j-insert-a",
    "j-insert-e",
    "j-insert-i",
    "j-insert-o",
    "j-insert-u",
    "j-kana-input",
    "j-start-henkan",
    "j-insert-comma",
    "j-insert-period",
    "j-purge-from-jisyo",
    "j-input-by-code-or-menu",
    "j-mode-off",
    "j-toggle-kana",
    "j-previous-candidate",
    "j-kakutei",
    "j-abbrev-input",
    "j-abbrev-period",
    "j-abbrev-comma",
    "j-zenkaku-eiji",
    "j-zenkaku-henkan",
    "j-today",
    "save-skkinput-local-jisyo",
    "vc-toggle-overthespot-like-input",
    "j-mode-off-and-self-insert",

    "j-kanainput-mode-on",
    "newline",
    "set-mark-command",
    "forward-char",
    "backward-char",
    "delete-char",
    "delete-backward-char",
    "j-try-comletion",
    "end-of-line",
    "beginning-of-line",
    "kill-line",
    "yank",
    "kill-region",
    "kill-ring-save",
    "exchange-point-and-mark",
    "previous-line",
    "next-line",
    "transpose-chars",
    "redraw",
    "prefix-char",
    "sendback-next-key",
    "sendback-key",
    "keyboard-quit",
    "close-skkinput",
    "vc-toggle-chatmode",
    "not-modified",
    "forward-word",
    "backward-word",
    "upcase-word",
    "downcase-word",
    "capitalize-word",
    "vc-toggle-eggnl",
    NULL,
  } ;
  /* どの文字列に一致するのかを調べる。*/
  func_no = 0 ;
  while( function_string_table[ func_no ] != NULL ){
    if( !myCharCharStrcmp
	( keyfunction, function_string_table[ func_no ] ) )
      return func_no ;
    func_no ++ ;
  }
  return ERR ;
}

/*
 * define-key-skkmap を解釈する関数。
 */
static int skkinput_analyze_define_key_skkmap
( struct myChar *oneline, int map )
{
  struct myChar *keyfunction ;
  struct myChar *keystring ;
  int func_no, ret = False, uselen ;

  /* まず、文字列のペアを解釈する。*/
  if( !skkinput_analyze_define_key
      ( oneline, &keystring, &keyfunction, &uselen ) )
    return False ;
  /* どの関数なのかを番号を見て調べる。*/
  if( ( func_no = skkinput_string2function_number( keyfunction ) ) < 0 )
    goto err_ret ;
  
  ret = skkinput_DefineKey( keystring, uselen, func_no, map ) ;
err_ret:
  free( keystring ) ;
  free( keyfunction ) ;
  return ret ;
}

/*
 * undefine-key-skkmap を解釈する関数。
 */
static int skkinput_analyze_undefine_key_skkmap
( struct myChar *oneline, int map )
{
  struct myChar *keystring, *keybuf ;
  int ret = False, uselen ;

  keystring = keybuf = NULL ;

  /* まず文字列をを解釈する。*/
  if( !skkinput_analyze_string( oneline, &keystring, False, False ) )
    goto err_ret ;
  /* この状態で keystring には、もとのままの文字列がコピーされてい
   * る。これを解釈しなければならない。*/
  uselen = parseKeyString( keystring, NULL, 0 ) ;
  /* 解釈に失敗した場合には捨てる。*/
  if( uselen <= 0 )
    goto err_ret ;
  if( ( keybuf = malloc( sizeof( struct myChar ) * uselen ) ) == NULL )
    goto err_ret ;
  ( void )parseKeyString( keystring, keybuf, uselen ) ;
  ret = skkinput_UndefineKey( keybuf, uselen, map ) ;
  free( keybuf ) ;
err_ret:
  if( keystring != NULL )
    free( keystring ) ;
  return ret ;
}

/*
 * lisp で書かれた行を解釈する関数。
 */
static int skkinput_analyze_skkinputlisp
( struct myChar *oneline )
{
  if( !skkinputlisp_weak_eval( oneline ) )
    return False ;
  return True ;
}

/*
 * 一行を解釈実行する関数。
 */
static int skkinput_analyze_line( struct myChar *oneline )
{
  int i, length ;
  struct myChar *wptr ;

  for( i = 0 ; skkinput_initdef[ i ].string != NULL ; i ++ ){
    /* 文字列が一致するかどうかを判断する。*/
    length = strlen( skkinput_initdef[ i ].string ) ;
    if( myCharCharStrncmp
	( oneline, skkinput_initdef[ i ].string, length ) )
      continue ;
    wptr = oneline + length ;

    /* 一致した文字列に対応するアクションを実行する。*/
    switch( skkinput_initdef[ i ].action ){
    case PARAM_BOOLEAN :
      return skkinput_analyze_boolean
	( wptr, skkinput_initdef[ i ].argument ) ;
    case PARAM_INTEGER :
      return skkinput_analyze_integer
	( wptr, skkinput_initdef[ i ].argument ) ;
    case PARAM_STRING :
      return skkinput_analyze_string
	( wptr, skkinput_initdef[ i ].argument, False, True ) ;
    case PARAM_PATH :
      return skkinput_analyze_string
	( wptr, skkinput_initdef[ i ].argument, True, True ) ;
    case PARAM_INPUT_VECTOR :
      return skkinput_analyze_input_vector
	( wptr, skkinput_initdef[ i ].argument ) ;
    case PARAM_ROM_KANA_RULE_LIST :
      return skkinput_analyze_rule_list
	( wptr, skkinput_initdef[ i ].argument ) ;
#ifdef THIS_IS_NECESSARY
    case PARAM_DEFINE_KEY_SKKMAP :
      return skkinput_analyze_define_key_skkmap( wptr, 0 ) ;
#endif
    case PARAM_DEFINE_KEY_SKKJMAP :
      return skkinput_analyze_define_key_skkmap( wptr, 1 ) ;
    case PARAM_DEFINE_KEY_ABBREVMAP :
      return skkinput_analyze_define_key_skkmap( wptr, 2 ) ;
#ifdef THIS_IS_NECESSARY
    case PARAM_UNDEFINE_KEY_SKKMAP :
      return skkinput_analyze_undefine_key_skkmap( wptr, 0 ) ;
#endif
    case PARAM_UNDEFINE_KEY_SKKJMAP :
      return skkinput_analyze_undefine_key_skkmap( wptr, 1 ) ;
    case PARAM_UNDEFINE_KEY_ABBREVMAP :
      return skkinput_analyze_undefine_key_skkmap( wptr, 2 ) ;
    case PARAM_SKKINPUTLISP :
      return skkinput_analyze_skkinputlisp( wptr ) ;
    default :
      return False ;
    }
  }
  return False ;
}

/*
 * skk-rom-kana-rule-list に相当する skkinput-rom-kana-rule-list を 
 * skkinput が解釈出来るように変換する関数。
 *---
 */
static struct skk_rom_kana_rule *makeSkkRomKanaRuleList
( struct skk_rom_kana_rule_list *top )
{
  int i ;
  struct skk_rom_kana_rule_list *node = top, *nNode ;
  struct skk_rom_kana_rule *rule_list = NULL ;

  if( node == NULL ){
    return skkinput_default_rom_kana_rule_list ;
  } else {
    for( i = 0 ; node != NULL ; i ++ )
      node = node->next ;
    /* 3 つはデフォルトでも持っているので +3 */
    rule_list = malloc( sizeof( struct skk_rom_kana_rule ) * ( i + 3 ) ) ;
    if( rule_list == NULL ){
      return NULL ;
    }
    node = top ;
    for( i = 0 ; node != NULL ; i ++ ){
      rule_list[ i ] = node->element ;
#ifdef DEBUG
      if( rule_list[ i ].state == NULL ){
	fprintf( stderr, "( \"(null)\", " ) ;
      } else {
	fprintf( stderr, "( \"" ) ;
	myCharFputstring( stderr, rule_list[ i ].state ) ;
	fprintf( stderr, "\", " ) ;
	fflush( stderr ) ;
      }
      if( rule_list[ i ].next == NULL ){
	fprintf( stderr, "\"(null)\", " ) ;
      } else {
	fprintf( stderr, "\"" ) ;
	myCharFputstring( stderr, rule_list[ i ].next ) ;
	fprintf( stderr, "\", " ) ;
	fflush( stderr ) ;
      }
      if( rule_list[ i ].houtput == NULL ){
	fprintf( stderr, "\"(null)\", " ) ;
      } else {
	fprintf( stderr, "\"" ) ;
	myCharFputstring( stderr, rule_list[ i ].houtput ) ;
	fprintf( stderr, "\", " ) ;
	fflush( stderr ) ;
      }
      if( rule_list[ i ].koutput == NULL ){
	fprintf( stderr, "\"(null)\" )\n" ) ;
      } else {
	fprintf( stderr, "\"" ) ;
	myCharFputstring( stderr, rule_list[ i ].koutput ) ;
	fprintf( stderr, "\" )\n" ) ;
	fflush( stderr ) ;
      }
#endif
      nNode = node->next ;
      free( node ) ;
      node = nNode ;
    }
    rule_list[ i++ ] = skkinput_default_rom_kana_rule_list[ 0 ] ;
    rule_list[ i++ ] = skkinput_default_rom_kana_rule_list[ 1 ] ;
    rule_list[ i++ ] = skkinput_default_rom_kana_rule_list[ 2 ] ;
  }
  return rule_list ;
}

/*
 * 設定ファイルを読み込む関数。
 *------
 * 一番強力なのは、-option での設定だと思う。だから、まず DEFAULT の設
 * 定、次にこの設定ファイル読み、最後に option で決定したい…けど…。
 * すると？ 設定ファイルが何処にあるのかを option で指定できなくならな
 * いか？ それは本意じゃないし…。
 */
int skkinput_readConfigFile( char *config_file )
{
  int chck = False, coding_system ;
  unsigned char *config_path ;
  /* 後藤慎也氏の patch …「行番号が一つ小さい」というのに対する patch。*/
  unsigned long line = 1 ;
  FILE *fp ;
  struct myChar *oneline ;

  skkinput_rom_kana_rule_list_top = NULL ;
  /* デフォルトのものを使用するよう指示された場合の処理。*/
  config_path = expand_file_name( config_file ) ;
  chck = True ;
  coding_system = check_skkinput_jisyo_code( config_path ) ;
  /* ファイルを開いてみる。*/
  if( ( fp = fopen( config_path, "rb" ) ) == NULL ){
    if( chck )
      free( config_path ) ;
    return False ;
  }
  /* ファイルが続く限り読み出す。*/
  while( !feof( fp ) ){
    /* 一行読み出す。*/
    if( ( oneline = mychar_readOneLine( fp, coding_system ) ) != NULL ){
      /* コメント行だった場合には無視する。*/
      if( !is_empty_line( oneline ) ){
	if( !skkinput_analyze_line( oneline ) ){
	  fprintf( stderr, "Warning: I ignore line %ld.\n", line ) ;
	}
      }
      free( oneline ) ;
      oneline = NULL ;
    }
    line ++ ;
  }
  fclose( fp ) ;
  /* メモリを解放して終了する。*/
  if( chck )
    free( config_path ) ;
  /* rom-kana rule list を設定する。*/
  skkinput_rom_kana_rule_list = 
    makeSkkRomKanaRuleList( skkinput_rom_kana_rule_list_top ) ;

  /* 
   * if skkserv_portnum is defined use skkserv_portnum for 
   * backward compatibility.
   */
  if (skkserv_portnum != 0) {
    if (skkserv_service) free(skkserv_service);
    skkserv_service = malloc(8); /* 8 digit is enough */
    sprintf(skkserv_service, "%u", skkserv_portnum);
  }
  if (skkserv_protocol_family != NULL) {
    if (strcasecmp(skkserv_protocol_family, "inet4") == 0
	|| strcasecmp(skkserv_protocol_family, "ipv4") == 0) {
      skkserv_pf = PF_INET;
    } else if (strcasecmp(skkserv_protocol_family, "inet6") == 0
	       || strcasecmp(skkserv_protocol_family, "ipv6") == 0) {
#if defined(USE_INET6)
      skkserv_pf = PF_INET6;
#else
      fprintf( stderr, "Warning: IPv6 not supported.\n" ) ;
#endif
    }
  }
  return True ;
}

static void initialize_skkinputDefaultVectorSub
( struct myChar *myCharVector[], unsigned char *charVector[] )
{
  unsigned char **cptr = charVector ;
  struct myChar **wptr = myCharVector ;
  int i ;

  for( i = 0 ; i < 128 ; i ++, cptr ++ , wptr ++ ){
    if( *cptr != NULL ){
      *wptr = malloc( sizeof( struct myChar ) * 2 ) ;
      ( *wptr )->charset = CHARSET_JISX0208_1983 ;
      ( *wptr )->chara   = 
	( ( ( ( *cptr )[ 0 ] ) << 8 ) | ( ( *cptr )[ 1 ] ) ) & 0x7F7F ;
      MYCHAR_SET_END_OF_STRING( ( ( *wptr )[ 1 ] ) ) ;
    } else {
      *wptr = NULL ;
    }
  }
  return ;
}

static void initialize_skkinputDefaultVectors( void )
{
  initialize_skkinputDefaultVectorSub
    ( skkinput_input_vector, skkinput_default_input_vector ) ;
  initialize_skkinputDefaultVectorSub
    ( skkinput_zenkaku_vector, skkinput_default_zenkaku_vector ) ;
  return ;
}

/*
 * 初期のローマ字仮名変換ルールを設定する関数。
 *---
 * 以前は char の string で指定していたので、変数宣言の場所で設定でき
 * たけど、今回から charset を変更してしまったので、それができなくなっ
 * てしまったがためにこうなりました。
 */
static void initialize_skkinputDefaultRomKanaRuleList( void )
{
  struct myChar *ptr ;

  ptr = malloc( sizeof( struct myChar ) * 3 ) ;
  myCharCharStrcpy( ptr, "nn" ) ;
  skkinput_default_rom_kana_rule_list[ 0 ].state = ptr ;
  ptr = malloc( sizeof( struct myChar ) * 2 ) ;
  ptr->charset = CHARSET_JISX0208_1983 ;
  ptr->chara   = 0x2473 ;
  MYCHAR_SET_END_OF_STRING( *( ptr + 1 ) ) ;
  skkinput_default_rom_kana_rule_list[ 0 ].houtput = ptr ;
  ptr = malloc( sizeof( struct myChar ) * 2 ) ;
  ptr->charset = CHARSET_JISX0208_1983 ;
  ptr->chara   = 0x2573 ;
  MYCHAR_SET_END_OF_STRING( *( ptr + 1 ) ) ;
  skkinput_default_rom_kana_rule_list[ 0 ].koutput = ptr ;

  ptr = malloc( sizeof( struct myChar ) * 3 ) ;
  myCharCharStrcpy( ptr, "n'" ) ;
  skkinput_default_rom_kana_rule_list[ 1 ].state = ptr ;
  ptr = malloc( sizeof( struct myChar ) * 2 ) ;
  ptr->charset = CHARSET_JISX0208_1983 ;
  ptr->chara   = 0x2473 ;
  MYCHAR_SET_END_OF_STRING( *( ptr + 1 ) ) ;
  skkinput_default_rom_kana_rule_list[ 1 ].houtput = ptr ;
  ptr = malloc( sizeof( struct myChar ) * 2 ) ;
  ptr->charset = CHARSET_JISX0208_1983 ;
  ptr->chara   = 0x2573 ;
  MYCHAR_SET_END_OF_STRING( *( ptr + 1 ) ) ;
  skkinput_default_rom_kana_rule_list[ 1 ].koutput = ptr ;
  return ;
}

/*
 * skkinput の動作記述をする変数を初期化する関数。
 *------
 * デフォルトの値を入れてしまう関数でもある。
 */
void initSkkinputDousaketteiVariables( void )
{
  struct servent *servent ;

  /* 局所辞書の所在を知らないとしておく。*/
  skkinput_local_jisyo_name  = DEFAULT_SKKLJISYO ;
  skkinput_backup_jisyo_name = DEFAULT_SKKBJISYO ;
  skkinput_record_name       = DEFAULT_SKKRECORD ;
  skk_local_jisyo_name	     = DEFAULT_SKKLOCALJISYO ;
  /* "/etc/services" からポート番号を得ておく。*/
  servent = getservbyname( SKKSERV_SERVICE_NAME, SKKSERV_SERVICE_PROTO ) ;
  if( servent == NULL ){
    /* /etc/services に設定はなかったので、config.h の中のを使う。*/
    skkserv_service          = strdup(DEFAULT_SKKPORT);
  } else {
    /* /etc/services に設定があった…。*/
    skkserv_service          = strdup( servent->s_name ) ;
#if defined(DEBUG)
    fprintf
      ( stderr, "getservbyname: \"%s\", %d\n",
	servent->s_name, skkserv_portnum ) ;
#endif
  }
  /* 環境変数 SKKSERV が設定されていれば、それを DEFAULT の skkserv *
   * host とする。*/
  if( ( skkserv_host = getenv( "SKKSERVER" ) ) == NULL )
    skkserv_host        = DEFAULT_SKKSERVER ;

  /* デフォルトのキーマップを作成する。*/
  skkinput_CompileDefaultKeyBind() ;
  /* 入力ベクトルの初期化を行う。*/
  initialize_skkinputDefaultVectors() ;
  /* egg 互換の newline をサポートするか否か。*/
  skk_egg_like_newline            = False ;
  /* dabbrev like の completion を行うかどうか。*/
  skkinput_dabbrev_like_completion= False ;
  /* chat-adapter-mode つまり newline を行頭だけでなく入れるか。*/
  skkinput_chatadaptermode        = False ;
  /* skk-local-jisyo を検索するかどうか。*/
  skkinput_search_skk_jisyo       = True ;
  /* skkinput-record を作成するかどうか。*/
  skkinput_keep_record            = True ;
  /* 日付の表示形式。*/
  skkinput_date_ad                = False ;
  skkinput_number_style           = False ;
  /* 削除キーは確定の機能を含んでいるのか否か。*/
  skkinput_delete_implies_kakutei = True ;
  /* 数字変換機能を利用する。*/
  skkinput_use_numeric_conversion = True ;
  skkinput_rom_kana_rule_list_top = NULL ;
  initialize_skkinputDefaultRomKanaRuleList() ;
  skkinput_rom_kana_rule_list     = skkinput_default_rom_kana_rule_list ;
  /* タブ幅。*/
  skkinput_tab_width              = 8 ;
  skkinput_rjj_like_input         = False ;
  /* オートセーブの期間を設定する。デフォルトは 10 秒である。*/
  skkinput_autosave_interval      = 10 * 1000 ;
  return ;
}

/*
 * 変換プロトコルなんかに関係なく SkkInputWidget を起こすにあたって必
 * 要な設定をする関数。
 */
int skkinput_setSkkInputValues( Widget gw )
{
  Arg arg[ 5 ] ;
  Cardinal i ;
  i = 0 ;
  /* 辞書の状態(Modified かどうか)。*/
  XtSetArg( arg[ i ], XtNjisyoDirty, skkinput_jisyo_dirty ) ;
  i ++ ;  
  /* egg-like-newline かどうか。*/
  XtSetArg( arg[ i ], XtNeggLikeNewline, skk_egg_like_newline ) ;
  i ++ ;  
  /* chat adapter モードかどうか。*/
  XtSetArg( arg[ i ], XtNchatAdapter, skkinput_chatadaptermode ) ;
  i ++ ;  
  XtSetValues( gw, arg, i ) ;
  return 0 ;
}
