(take_action_list): Move the handling of candidate-list
[m17n/m17n-lib.git] / src / input.c
index fb864e5..2de5547 100644 (file)
@@ -1,5 +1,5 @@
 /* input.c -- input method module.
-   Copyright (C) 2003, 2004, 2005, 2006
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H15PRO112
 
@@ -17,7 +17,7 @@
 
    You should have received a copy of the GNU Lesser General Public
    License along with the m17n library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    02111-1307, USA.  */
 
 /***en
     input event to an input key by himself.  See the documentation of
     the function minput_event_to_key () for the detail.
 
-    <li> Foreign Input Method
+    <li> Foreign Input Method @anchor foreign-input-method
 
     A foreign input method has @c Mnil LANGUAGE, and its body is
     defined in an external resource (e.g. XIM of X Window System).
     For this kind of input methods, the symbol NAME must have a
-    property of key @c Minput_driver, and the value must be a pointer
+    property of key #Minput_driver, and the value must be a pointer
     to an input method driver.  Therefore, by preparing a proper
     driver, any kind of input method can be treated in the framework
     of the @c m17n @c library.
 
     For convenience, the m17n-X library provides an input method
     driver that enables the input style of OverTheSpot for XIM, and
-    stores @c Minput_driver property of the symbol @c Mxim with a
+    stores #Minput_driver property of the symbol @c Mxim with a
     pointer to the driver.  See the documentation of m17n GUI API for
     the detail.
 
     <ul> 
     <li> ÆâÉôÆþÎϥ᥽¥Ã¥É
 
-    ÆâÉôÆþÎϥ᥽¥Ã¥É¤È¤Ï LANGUAGE ¤¬ @c Mnil °Ê³°¤Î¤â¤Î¤Ç¤¢¤ê¡¢¤½¤ÎËÜÂΤÏm17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë
-    <Minput_method, LANGUAGE, NAME> 
-    ¤È¤¤¤¦¥¿¥°¤òÉÕ¤±¤ÆÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£
-    ¤³¤Î¼ï¤ÎÆþÎϥ᥽¥Ã¥É¤ËÂФ·¤Æ¡¢m17n ¥é¥¤¥Ö¥é¥ê¤Ç¤Ï
-    CUI ÍѤȠGUI ÍѤ½¤ì¤¾¤ì¤ÎÆþÎϥ᥽¥Ã¥É¥É¥é¥¤¥Ð¤ò¤¢¤é¤«¤¸¤áÄêµÁ¤·¤Æ¤¤¤ë¡£
-    ¤³¤ì¤é¤Î¥É¥é¥¤¥Ð¤Ï m17n ¥é¥¤¥Ö¥é¥ê¼«ÂΤÎÆþÎϽèÍý¥¨¥ó¥¸¥ó¤òÍøÍѤ¹¤ë¡£
-    m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤Ï¡¢ÆÃÄê¤Î¸À¸ìÀìÍѤǤʤ¤ÆþÎϥ᥽¥Ã¥É¤òÄêµÁ¤¹¤ë¤³¤È¤â¤Ç¤­¡¢
-    ¤½¤Î¤è¤¦¤ÊÆþÎϥ᥽¥Ã¥É¤Î LANGUAGE ¤Ï @c Mt ¤Ç¤¢¤ë¡£
-
-    ÆâÉôÆþÎϥ᥽¥Ã¥É¤Ï¡¢¥æ¡¼¥¶¤ÎÆþÎÏ¥¤¥Ù¥ó¥È¤ËÂбþ¤·¤¿¥·¥ó¥Ü¥ë¤Ç¤¢¤ëÆþÎÏ¥­¡¼¤ò¼õ¤±¼è¤ë¡£
-    @c m17n @c ¥é¥¤¥Ö¥é¥ê ¤ÏÆþÎÏ¥¤¥Ù¥ó¥È¤¬¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ç¤É¤¦É½¸½¤µ¤ì¤Æ¤¤¤ë¤«¤òÃΤ뤳¤È¤¬¤Ç¤­¤Ê¤¤¤Î¤Ç¡¢
-    ÆþÎÏ¥¤¥Ù¥ó¥È¤«¤éÆþÎÏ¥­¡¼¤Ø¤ÎÊÑ´¹¤Ï¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥Þ¤ÎÀÕǤ¤Ç¹Ô¤ï¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
-    ¾ÜºÙ¤Ë¤Ä¤¤¤Æ¤Ï´Ø¿ô minput_event_to_key () ¤ÎÀâÌÀ¤ò»²¾È¡£
-
-    <li> ³°ÉôÆþÎϥ᥽¥Ã¥É
-
-    ³°ÉôÆþÎϥ᥽¥Ã¥É¤È¤Ï LANGUAGE ¤¬ @c Mnil ¤Î¤â¤Î¤Ç¤¢¤ê¡¢¤½¤ÎËÜÂΤϳ°Éô¤Î¥ê¥½¡¼¥¹¤È¤·¤ÆÄêµÁ¤µ¤ì¤ë¡£
-    ¡Ê¤¿¤È¤¨¤ÐX Window System ¤ÎXIM ¤Ê¤É¡£) 
-    ¤³¤Î¼ï¤ÎÆþÎϥ᥽¥Ã¥É¤Ç¤Ï¡¢¥·¥ó¥Ü¥ë NAME ¤Ï@c Minput_driver 
-    ¤ò¥­¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Á¡¢¤½¤ÎÃͤÏÆþÎϥ᥽¥Ã¥É¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£
-    ¤³¤Î¤³¤È¤Ë¤è¤ê¡¢Å¬Àڤʥɥ饤¥Ð¤ò½àÈ÷¤¹¤ë¤³¤È¤Ë¤è¤Ã¤Æ¡¢¤¤¤«¤Ê¤ë¼ïÎà¤ÎÆþÎϥ᥽¥Ã¥É¤â
-    @c m17n @c ¥é¥¤¥Ö¥é¥ê ¤ÎÏÈÁȤÎÃæ¤Ç°·¤¦»ö¤¬¤Ç¤­¤ë¡£
-
-    ÍøÊØÀ­¤Î´ÑÅÀ¤«¤é¡¢m17n X ¥é¥¤¥Ö¥é¥ê¤Ï XIM ¤Î OverTheSpot 
-    ¤ÎÆþÎÏ¥¹¥¿¥¤¥ë¤ò¼Â¸½¤¹¤ëÆþÎϥ᥽¥Ã¥É¥É¥é¥¤¥Ð¤òÄ󶡤·¡¢¤Þ¤¿¥·¥ó¥Ü¥ë @c Mxim ¤Î 
-    @c Minput_driver ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ¤½¤Î¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÝ»ý¤·¤Æ¤¤¤ë¡£
-    ¾ÜºÙ¤Ë¤Ä¤¤¤Æ¤Ï m17n GUI API ¤Î¥É¥­¥å¥á¥ó¥È¤ò»²¾È¤Î¤³¤È¡£
+    ÆâÉôÆþÎϥ᥽¥Ã¥É¤È¤Ï LANGUAGE ¤¬ @c Mnil °Ê³°¤Î¤â¤Î¤Ç¤¢¤ê¡¢¤½¤ÎËÜÂÎ
+    ¤Ïm17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë<Minput_method, LANGUAGE, NAME> ¤È¤¤¤¦¥¿¥°¤òÉÕ
+    ¤±¤ÆÄêµÁ¤µ¤ì¤Æ¤¤¤ë¡£¤³¤Î¼ï¤ÎÆþÎϥ᥽¥Ã¥É¤ËÂФ·¤Æ¡¢m17n ¥é¥¤¥Ö¥é¥ê¤Ç
+    ¤ÏCUI ÍѤȠGUI ÍѤ½¤ì¤¾¤ì¤ÎÆþÎϥ᥽¥Ã¥É¥É¥é¥¤¥Ð¤ò¤¢¤é¤«¤¸¤áÄêµÁ¤·¤Æ
+    ¤¤¤ë¡£¤³¤ì¤é¤Î¥É¥é¥¤¥Ð¤Ï m17n ¥é¥¤¥Ö¥é¥ê¼«ÂΤÎÆþÎϽèÍý¥¨¥ó¥¸¥ó¤òÍø
+    ÍѤ¹¤ë¡£m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤Ï¡¢ÆÃÄê¤Î¸À¸ìÀìÍѤǤʤ¤ÆþÎϥ᥽¥Ã¥É¤òÄê
+    µÁ¤¹¤ë¤³¤È¤â¤Ç¤­¡¢¤½¤Î¤è¤¦¤ÊÆþÎϥ᥽¥Ã¥É¤Î LANGUAGE ¤Ï @c Mt ¤Ç¤¢¤ë¡£
+
+    ÆâÉôÆþÎϥ᥽¥Ã¥É¤Ï¡¢¥æ¡¼¥¶¤ÎÆþÎÏ¥¤¥Ù¥ó¥È¤ËÂбþ¤·¤¿¥·¥ó¥Ü¥ë¤Ç¤¢¤ëÆþ
+    ÎÏ¥­¡¼¤ò¼õ¤±¼è¤ë¡£@c m17n @c ¥é¥¤¥Ö¥é¥ê ¤ÏÆþÎÏ¥¤¥Ù¥ó¥È¤¬¥¢¥×¥ê¥±¡¼
+    ¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ç¤É¤¦É½¸½¤µ¤ì¤Æ¤¤¤ë¤«¤òÃΤ뤳¤È¤¬¤Ç¤­¤Ê¤¤¤Î¤Ç¡¢Æþ
+    ÎÏ¥¤¥Ù¥ó¥È¤«¤éÆþÎÏ¥­¡¼¤Ø¤ÎÊÑ´¹¤Ï¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥Þ¤ÎÀÕǤ¤Ç
+    ¹Ô¤ï¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¾ÜºÙ¤Ë¤Ä¤¤¤Æ¤Ï´Ø¿ô minput_event_to_key () ¤Î
+    ÀâÌÀ¤ò»²¾È¡£
+
+    <li> ³°ÉôÆþÎϥ᥽¥Ã¥É @anchor foreign-input-method
+
+    ³°ÉôÆþÎϥ᥽¥Ã¥É¤È¤Ï LANGUAGE ¤¬ @c Mnil ¤Î¤â¤Î¤Ç¤¢¤ê¡¢¤½¤ÎËÜÂΤϳ°
+    Éô¤Î¥ê¥½¡¼¥¹¤È¤·¤ÆÄêµÁ¤µ¤ì¤ë¡£¡Ê¤¿¤È¤¨¤ÐX Window System ¤ÎXIM ¤Ê
+    ¤É¡£) ¤³¤Î¼ï¤ÎÆþÎϥ᥽¥Ã¥É¤Ç¤Ï¡¢¥·¥ó¥Ü¥ë NAME ¤Ï #Minput_driver ¤ò
+    ¥­¡¼¤È¤¹¤ë¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Á¡¢¤½¤ÎÃͤÏÆþÎϥ᥽¥Ã¥É¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó
+    ¥¿¤Ç¤¢¤ë¡£¤³¤Î¤³¤È¤Ë¤è¤ê¡¢Å¬Àڤʥɥ饤¥Ð¤ò½àÈ÷¤¹¤ë¤³¤È¤Ë¤è¤Ã¤Æ¡¢¤¤
+    ¤«¤Ê¤ë¼ïÎà¤ÎÆþÎϥ᥽¥Ã¥É¤â@c m17n @c ¥é¥¤¥Ö¥é¥ê ¤ÎÏÈÁȤÎÃæ¤Ç°·¤¦»ö
+    ¤¬¤Ç¤­¤ë¡£
+
+    ÍøÊØÀ­¤Î´ÑÅÀ¤«¤é¡¢m17n X ¥é¥¤¥Ö¥é¥ê¤Ï XIM ¤Î OverTheSpot ¤ÎÆþÎÏ¥¹¥¿
+    ¥¤¥ë¤ò¼Â¸½¤¹¤ëÆþÎϥ᥽¥Ã¥É¥É¥é¥¤¥Ð¤òÄ󶡤·¡¢¤Þ¤¿¥·¥ó¥Ü¥ë @c Mxim ¤Î
+    #Minput_driver ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤȤ·¤Æ¤½¤Î¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÝ»ý
+    ¤·¤Æ¤¤¤ë¡£¾ÜºÙ¤Ë¤Ä¤¤¤Æ¤Ï m17n GUI API ¤Î¥É¥­¥å¥á¥ó¥È¤ò»²¾È¤Î¤³¤È¡£
 
     </ul> 
 
 #include <dirent.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <time.h>
 
 #include "config.h"
 
 #include <dlfcn.h>
 #endif
 
-#include "m17n-gui.h"
+#include "m17n.h"
 #include "m17n-misc.h"
 #include "internal.h"
 #include "mtext.h"
 #include "database.h"
 #include "charset.h"
 
-static int mdebug_mask = MDEBUG_INPUT;
+static int mdebug_flag = MDEBUG_INPUT;
 
-static MSymbol Minput_method;
+static int fully_initialized;
 
 /** Symbols to load an input method data.  */
 static MSymbol Mtitle, Mmacro, Mmodule, Mstate, Minclude;
 
 /** Symbols for actions.  */
 static MSymbol Minsert, Mdelete, Mmark, Mmove, Mpushback, Mundo, Mcall, Mshift;
-static MSymbol Mselect, Mshow, Mhide, Mcommit, Munhandle;
+static MSymbol Mselect, Mshow, Mhide, Mcommit, Munhandle, Mpop;
 static MSymbol Mset, Madd, Msub, Mmul, Mdiv, Mequal, Mless, Mgreater;
 static MSymbol Mless_equal, Mgreater_equal;
 static MSymbol Mcond;
-static MSymbol Mplus, Mminus, Mstar, Mslush, Mand, Mor, Mnot;
+static MSymbol Mplus, Mminus, Mstar, Mslash, Mand, Mor, Mnot;
+
+/** Special action symbol.  */
+static MSymbol Mat_reload;
 
 static MSymbol M_candidates;
 
@@ -189,14 +195,16 @@ static MSymbol one_char_symbol[256];
 
 static MSymbol M_key_alias;
 
-static MSymbol M_description, M_command, M_variable;
+static MSymbol Mdescription, Mcommand, Mvariable, Mglobal, Mconfig;
+
+static MSymbol M_gettext;
 
 /** Structure to hold a map.  */
 
 struct MIMMap
 {
   /** List of actions to take when we reach the map.  In a root map,
-      the actions are executed only when there's no more key.  */
+      the actions are executed only when there is no more key.  */
   MPlist *map_actions;
 
   /** List of deeper maps.  If NULL, this is a terminal map.  */
@@ -212,6 +220,7 @@ typedef MPlist *(*MIMExternalFunc) (MPlist *plist);
 
 typedef struct
 {
+  MSymbol name;
   void *handle;
   MPlist *func_list;           /* function name vs (MIMExternalFunc *) */
 } MIMExternalModule;
@@ -231,246 +240,319 @@ struct MIMState
   MIMMap *map;
 };
 
-/* Lookup keys KEY1,2,3 in the nested plist PLIST, and return the
-   value.  */
+#define CUSTOM_FILE "config.mic"
 
-static MPlist *
-lookup_nested_list (MPlist *plist, MSymbol key1, MSymbol key2, MSymbol key3)
-{
-  MSymbol key[3];
-  int i;
+static MPlist *load_im_info_keys;
 
-  key[0] = key1, key[1] = key2, key[2] = key3;
-  for (i = 0; i < 3; i++)
-    {
-      plist = mplist_find_by_value (plist, key[i]);
-      if (! plist)
-       return NULL;
-      plist = MPLIST_NEXT (plist);
-      plist = MPLIST_PLIST (plist);
-    }
-  return plist;
-}
+/* List of input method information.  The format is:
+     (LANGUAGE NAME t:IM_INFO ... ... ...)  */
+static MPlist *im_info_list;
 
-/* Set VAL for keys KEY1,2,3 in the nested plist PLIST.  */
+/* Database for user's customization file.  */
+static MDatabase *im_custom_mdb;
 
-static MPlist *
-set_nested_list (MPlist *plist, MSymbol key1, MSymbol key2, MSymbol key3,
-                MPlist *val)
+/* List of input method information loaded from im_custom_mdb.  The
+   format is the same as im_info_list.  */
+static MPlist *im_custom_list;
+
+/* List of input method information configured by
+   minput_config_command and minput_config_variable.  The format is
+   the same as im_info_list.  */
+static MPlist *im_config_list;
+
+/* Global input method information.  It points into the element of
+   im_info_list corresponding to LANGUAGE == `nil' and NAME ==
+   `global'.  */
+static MInputMethodInfo *global_info;
+
+static int update_global_info (void);
+static int update_custom_info (void);
+static MInputMethodInfo *get_im_info (MSymbol, MSymbol, MSymbol, MSymbol);
+
+\f
+static void
+fully_initialize ()
 {
-  MSymbol key[3];
-  int i;
-  MPlist *pl;
+  char *key_names[32]
+    = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       "BackSpace", "Tab", "Linefeed", "Clear", NULL, "Return", NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, "Escape", NULL, NULL, NULL, NULL };
+  char buf[6], buf2[32], buf3[2];
+  int i, j;
+  /* Maximum case: '\215', C-M-m, C-M-M, M-Return, C-A-m, C-A-M, A-Return
+     plus one for cyclic alias.  */
+  MSymbol alias[8];
+
+  M_key_alias = msymbol ("  key-alias");
 
-  key[0] = key1, key[1] = key2, key[2] = key3;
-  for (i = 0; i < 3; i++)
+  buf3[1] = '\0';
+
+  /* Aliases for 0x00-0x1F */
+  buf[0] = 'C';
+  buf[1] = '-';
+  buf[3] = '\0';
+  for (i = 0, buf[2] = '@'; i < ' '; i++, buf[2]++)
     {
-      pl = mplist_find_by_value (plist, key[i]);
-      if (pl)
+      j = 0;
+      buf3[0] = i;
+      alias[j++] = msymbol (buf3);
+      alias[j++] = one_char_symbol[i] = msymbol (buf);
+      if (key_names[i] || (buf[2] >= 'A' && buf[2] <= 'Z'))
        {
-         pl = MPLIST_NEXT (pl);
-         plist = MPLIST_PLIST (pl);
+         if (key_names[i])
+           {
+             /* Ex: `Escape' == `C-['  */
+             alias[j++] = msymbol (key_names[i]);
+           }
+         if (buf[2] >= 'A' && buf[2] <= 'Z')
+           {
+             /* Ex: `C-a' == `C-A'  */
+             buf[2] += 32;
+             alias[j++] = msymbol (buf);
+             buf[2] -= 32;
+           }
        }
-      else
+      /* Establish cyclic alias chain.  */
+      alias[j] = alias[0];
+      while (--j >= 0)
+       msymbol_put (alias[j], M_key_alias, alias[j + 1]);
+    }
+
+  /* Aliases for 0x20-0x7E */
+  buf[0] = 'S';
+  for (i = buf[2] = ' '; i < 127; i++, buf[2]++)
+    {
+      one_char_symbol[i] = msymbol (buf + 2);
+      if (i >= 'A' && i <= 'Z')
        {
-         pl = mplist_add (plist, Msymbol, key[i]);
-         plist = mplist ();
-         pl = mplist_add (pl, Mplist, plist);
-         M17N_OBJECT_UNREF (plist);
+         /* Ex: `A' == `S-A' == `S-a'.  */
+         alias[0] = alias[3] = one_char_symbol[i];
+         alias[1] = msymbol (buf);
+         buf[2] += 32;
+         alias[2] = msymbol (buf);
+         buf[2] -= 32;
+         for (j = 0; j < 3; j++)
+           msymbol_put (alias[j], M_key_alias, alias[j + 1]);
        }
     }
-  mplist_set (pl, Mplist, val);
-  M17N_OBJECT_UNREF (val);
-  return pl;
-}
-
-/* Parse PLIST as a value of nested list and return an adjusted list.
-
-   PLIST has this form;
-     (symbol:command
-      plist:(symbol:KEY
-            [ mtext:DESCRIPTION | symbol:nil ]
-            ;; The remaining elements are checked CHECK_FUNC.
-            ...)
-      plist:(symbol:KEY
-            [ mtext:DESCRIPTION | symbol:nil ]
-            ;; The remaining elements are checked CHECK_FUNC.
-            ...)
-      ...)
-
-   GLOBAL is a global list.  If a description text is missing, it is
-   extracted from GLOBAL.
 
-   The return value is a plist of this format:
-     (symbol:KEY
-      plist:([ mtext:DESCRIPTION | symbol:nil ]
-            ...)
-      symbol:KEY
-      plist:([ mtext:DESCRIPTION | symbol:nil ]
-            ...)
-      ...)
-
-   PLIST itself is unref-ed.  */
-
-static MPlist *
-parse_nested_list_value (MPlist *plist, MPlist *global, MSymbol key,
-                        int (*check_func) (MPlist *))
-{
-  MPlist *val, *pl, *p, *p0;
+  /* Aliases for 0x7F */
+  buf3[0] = 0x7F;
+  alias[0] = alias[3] = msymbol (buf3);
+  alias[1] = one_char_symbol[127] = msymbol ("Delete");
+  alias[2] = msymbol ("C-?");
+  for (j = 0; j < 3; j++)
+    msymbol_put (alias[j], M_key_alias, alias[j + 1]);
 
-  val = mplist ();
-  if (! MPLIST_PLIST_P (plist))
+  /* Aliases for 0x80-0x9F */
+  buf[0] = 'C';
+  /* buf[1] = '-'; -- already done */
+  buf[3] = '-';
+  buf[5] = '\0';
+  buf2[1] = '-';
+  for (i = 128, buf[4] = '@'; i < 160; i++, buf[4]++)
     {
-      M17N_OBJECT_UNREF (plist);
-      return val;
+      j = 0;
+      buf3[0] = i;
+      alias[j++] = msymbol (buf3);
+      /* `C-M-a' == `C-A-a' */
+      buf[2] = 'M';
+      alias[j++] = one_char_symbol[i] = msymbol (buf);
+      buf[2] = 'A';
+      alias[j++] = msymbol (buf);
+      if (key_names[i - 128])
+       {
+         /* Ex: `M-Escape' == `A-Escape' == `C-M-['.  */
+         buf2[0] = 'M';
+         strcpy (buf2 + 2, key_names[i - 128]);
+         alias[j++] = msymbol (buf2);
+         buf2[0] = 'A';
+         alias[j++] = msymbol (buf2);
+       }
+      if (buf[4] >= 'A' && buf[4] <= 'Z')
+       {
+         /* Ex: `C-M-a' == `C-M-A'.  */
+         buf[4] += 32;
+         buf[2] = 'M';
+         alias[j++] = msymbol (buf);
+         buf[2] = 'A';
+         alias[j++] = msymbol (buf);
+         buf[4] -= 32;
+       }
+
+      /* Establish cyclic alias chain.  */
+      alias[j] = alias[0];
+      while (--j >= 0)
+       msymbol_put (alias[j], M_key_alias, alias[j + 1]);
     }
-  pl = MPLIST_PLIST (plist);
-  if (! MPLIST_SYMBOL_P (pl)
-      || MPLIST_SYMBOL (pl) != key)
+
+  /* Aliases for 0xA0-0xFF */
+  for (i = 160, buf[4] = ' '; i < 255; i++, buf[4]++)
     {
-      M17N_OBJECT_UNREF (plist);
-      return val;
+      j = 0;
+      buf3[0] = i;
+      alias[j++] = msymbol (buf3);
+      buf[2] = 'M';
+      alias[j++] = one_char_symbol[i] = msymbol (buf + 2);
+      buf[2] = 'A';
+      alias[j++] = msymbol (buf + 2);
+      alias[j]= alias[0];
+      while (--j >= 0)
+       msymbol_put (alias[j], M_key_alias, alias[j + 1]);
     }
 
-  MPLIST_DO (pl, MPLIST_NEXT (pl))
+  buf3[0] = (char) 255;
+  alias[0] = alias[3] = msymbol (buf3);
+  alias[1] = one_char_symbol[255] = msymbol ("M-Delete");
+  alias[2] = msymbol ("A-Delete");
+  for (j = 0; j < 3; j++)
+    msymbol_put (alias[j], M_key_alias, alias[j + 1]);
+
+  /* Aliases for keys that can't be mapped to one-char-symbol
+     (e.g. C-A-1) */
+  /* buf is already set to "C-?-".  */
+  for (i = ' '; i <= '~'; i++)
     {
-      MSymbol name;
-      MPlist *global_def = NULL;
-
-      if (! MPLIST_PLIST_P (pl))
-       continue;
-      p = MPLIST_PLIST (pl);
-      if (! MPLIST_SYMBOL_P (p))
-       continue;
-      name = MPLIST_SYMBOL (p);
-      p = MPLIST_NEXT (p);
-      if (MPLIST_TAIL_P (p))
+      if (i == '@')
        {
-         if (! global)
-           continue;
-         global_def = mplist_find_by_value (global, name);
-         if (! global_def)
-           continue;
-         global_def = MPLIST_PLIST (MPLIST_NEXT (global_def));
-         mplist__conc (p, global_def);
+         i = '_';
+         continue;
        }
-      p0 = MPLIST_NEXT (p);
-      if (MPLIST_TAIL_P (p0))
+      if (i == 'a')
        {
-         if (! global || global_def)
-           continue;
-         global_def = mplist_find_by_value (global, name);
-         if (! global_def)
-           continue;
-         global_def = MPLIST_PLIST (MPLIST_NEXT (global_def));
-         global_def = MPLIST_NEXT (global_def);
-         if (MPLIST_TAIL_P (global_def))
-           continue;
-         mplist__conc (p0, global_def);
+         i = 'z';
+         continue;
        }
-      if ((*check_func) (p0) < 0)
-       continue;
-      mplist_add (val, Msymbol, name);
-      mplist_add (val, Mplist, p);
+      buf[2] = 'M';
+      buf[4] = i;
+      alias[0] = alias[2] = msymbol (buf);
+      buf[2] = 'A';
+      alias[1] = msymbol (buf);
+      for (j = 0; j < 2; j++)
+       msymbol_put (alias[j], M_key_alias, alias[j + 1]);
     }
 
-  M17N_OBJECT_UNREF (plist);
-  return val;
-}
+  Minput_method = msymbol ("input-method");
+  Mtitle = msymbol ("title");
+  Mmacro = msymbol ("macro");
+  Mmodule = msymbol ("module");
+  Mmap = msymbol ("map");
+  Mstate = msymbol ("state");
+  Minclude = msymbol ("include");
+  Minsert = msymbol ("insert");
+  M_candidates = msymbol ("  candidates");
+  Mdelete = msymbol ("delete");
+  Mmove = msymbol ("move");
+  Mmark = msymbol ("mark");
+  Mpushback = msymbol ("pushback");
+  Mpop = msymbol ("pop");
+  Mundo = msymbol ("undo");
+  Mcall = msymbol ("call");
+  Mshift = msymbol ("shift");
+  Mselect = msymbol ("select");
+  Mshow = msymbol ("show");
+  Mhide = msymbol ("hide");
+  Mcommit = msymbol ("commit");
+  Munhandle = msymbol ("unhandle");
+  Mset = msymbol ("set");
+  Madd = msymbol ("add");
+  Msub = msymbol ("sub");
+  Mmul = msymbol ("mul");
+  Mdiv = msymbol ("div");
+  Mequal = msymbol ("=");
+  Mless = msymbol ("<");
+  Mgreater = msymbol (">");
+  Mless_equal = msymbol ("<=");
+  Mgreater_equal = msymbol (">=");
+  Mcond = msymbol ("cond");
+  Mplus = msymbol ("+");
+  Mminus = msymbol ("-");
+  Mstar = msymbol ("*");
+  Mslash = msymbol ("/");
+  Mand = msymbol ("&");
+  Mor = msymbol ("|");
+  Mnot = msymbol ("!");
 
-static MPlist *variable_list, *command_list;
-static int check_variable_list (MPlist *plist);
-static int check_command_list (MPlist *plist);
-static MPlist *load_partial_im_info (MSymbol language, MSymbol name,
-                                    MSymbol extra, MSymbol key);
+  Mat_reload = msymbol ("-reload");
 
-static MPlist *
-get_nested_list (MSymbol language, MSymbol name, MSymbol extra, MSymbol key)
-{
-  MPlist *total_list;
-  int (*check_func) (MPlist *);
-  MPlist *plist, *global;
+  Mcandidates_group_size = msymbol ("candidates-group-size");
+  Mcandidates_charset = msymbol ("candidates-charset");
 
-  if (key == M_variable)
-    {
-      if (! variable_list)
-       variable_list = mplist ();
-      total_list = variable_list;
-      check_func = check_variable_list;
-    }
-  else
-    {
-      if (! command_list)
-       command_list = mplist ();
-      total_list = command_list;
-      check_func = check_command_list;
-    }
+  Mcandidate_list = msymbol_as_managing_key ("  candidate-list");
+  Mcandidate_index = msymbol ("  candidate-index");
 
-  if (MPLIST_TAIL_P (total_list))
-    {
-      plist = load_partial_im_info (Mt, Mnil, key, key);
-      if (plist)
-       global = parse_nested_list_value (plist, NULL, key, check_func);
-      else
-       global = mplist ();
-      set_nested_list (total_list, Mt, Mnil, key, global);
-    }
-  else
-    global = lookup_nested_list (total_list, Mt, Mnil, key);
+  Minit = msymbol ("init");
+  Mfini = msymbol ("fini");
 
-  if (name == Mnil)
-    return global;
+  Mdescription = msymbol ("description");
+  Mcommand = msymbol ("command");
+  Mvariable = msymbol ("variable");
+  Mglobal = msymbol ("global");
+  Mconfig = msymbol ("config");
+  M_gettext = msymbol ("_");
 
-  plist = lookup_nested_list (total_list, language, name, extra);
-  if (plist)
-    return plist;
+  load_im_info_keys = mplist ();
+  mplist_add (load_im_info_keys, Mstate, Mnil);
+  mplist_push (load_im_info_keys, Mmap, Mnil);
 
-  plist = load_partial_im_info (language, name, extra, key);
-  if (plist)
-    plist = parse_nested_list_value (plist, global, key, check_func);
-  else
-    plist = mplist ();
-  set_nested_list (total_list, language, name, extra, plist);
-  return plist;
+  im_info_list = mplist ();
+  im_config_list = im_custom_list = NULL;
+  im_custom_mdb = NULL;
+  update_custom_info ();
+  global_info = NULL;
+  update_global_info ();
+
+  fully_initialized = 1;
 }
 
+#define MINPUT__INIT()         \
+  do {                         \
+    if (! fully_initialized)   \
+      fully_initialize ();     \
+  } while (0)
+
+\f
 static int
-marker_code (MSymbol sym)
+marker_code (MSymbol sym, int surrounding)
 {
   char *name;
 
   if (sym == Mnil)
     return -1;
   name = MSYMBOL_NAME (sym);
-  return ((name[0] == '@'
-          && ((name[1] >= '0' && name[1] <= '9')
-              || name[1] == '<' || name[1] == '>'
-              || name[1] == '=' || name[1] == '+' || name[1] == '-'
-              || name[1] == '[' || name[1] == ']'
-              || name[1] == '@')
-          && name[2] == '\0')
-         ? name[1] : -1);
+  return (name[0] != '@' ? -1
+         : (((name[1] >= '0' && name[1] <= '9')
+             || name[1] == '<' || name[1] == '>' || name[1] == '='
+             || name[1] == '[' || name[1] == ']'
+             || name[1] == '@')
+            && name[2] == '\0') ? name[1]
+         : (name[1] != '+' && name[1] != '-') ? -1
+         : (name[2] == '\0' || surrounding) ? name[1]
+         : -1);
 }
 
 
+/* Return a plist containing an integer value of VAR.  The plist must
+   not be UNREFed. */
+
 static MPlist *
 resolve_variable (MInputContextInfo *ic_info, MSymbol var)
 {
-  MPlist *p;
+  MPlist *plist = mplist__assq (ic_info->vars, var);
 
-  MPLIST_DO (p, ic_info->vars)
-    {
-      if (MPLIST_SYMBOL (p) == var)
-       break;
-      p = MPLIST_NEXT (p);
-    }
-  if (MPLIST_TAIL_P (p))
+  if (plist)
     {
-      p = ic_info->vars;
-      mplist_push (p, Minteger, (void *) 0);
-      mplist_push (p, Msymbol, var);
+      plist = MPLIST_PLIST (plist);
+      return MPLIST_NEXT (plist);
     }
-  return (MPLIST_NEXT (p));
+
+  plist = mplist ();
+  mplist_push (ic_info->vars, Mplist, plist);
+  M17N_OBJECT_UNREF (plist);
+  plist = mplist_add (plist, Msymbol, var);
+  plist = mplist_add (plist, Minteger, (void *) 0);
+  return plist;
 }
 
 static MText *
@@ -479,8 +561,8 @@ get_surrounding_text (MInputContext *ic, int len)
   MText *mt = NULL;
 
   mplist_push (ic->plist, Minteger, (void *) len);
-  minput__callback (ic, Minput_get_surrounding_text);
-  if (MPLIST_MTEXT_P (ic->plist))
+  if (minput_callback (ic, Minput_get_surrounding_text) >= 0
+      && MPLIST_MTEXT_P (ic->plist))
     mt = MPLIST_MTEXT (ic->plist);
   mplist_pop (ic->plist);
   return mt;
@@ -492,12 +574,18 @@ delete_surrounding_text (MInputContext *ic, int pos)
   MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
 
   mplist_push (ic->plist, Minteger, (void *) pos);
-  minput__callback (ic, Minput_delete_surrounding_text);
+  minput_callback (ic, Minput_delete_surrounding_text);
   mplist_pop (ic->plist);
   if (pos < 0)
-    M17N_OBJECT_UNREF (ic_info->preceding_text);
+    {
+      M17N_OBJECT_UNREF (ic_info->preceding_text);
+      ic_info->preceding_text = NULL;
+    }
   else if (pos > 0)
-    M17N_OBJECT_UNREF (ic_info->following_text);
+    {
+      M17N_OBJECT_UNREF (ic_info->following_text);
+      ic_info->following_text = NULL;
+    }
 }
 
 static int
@@ -507,7 +595,7 @@ get_preceding_char (MInputContext *ic, int pos)
   MText *mt;
   int len;
 
-  if (ic_info->preceding_text)
+  if (pos && ic_info->preceding_text)
     {
       len = mtext_nchars (ic_info->preceding_text);
       if (pos <= len)
@@ -515,7 +603,7 @@ get_preceding_char (MInputContext *ic, int pos)
     }
   mt = get_surrounding_text (ic, - pos);
   if (! mt)
-    return -1;
+    return -2;
   len = mtext_nchars (mt);
   if (ic_info->preceding_text)
     {
@@ -524,6 +612,8 @@ get_preceding_char (MInputContext *ic, int pos)
          M17N_OBJECT_UNREF (ic_info->preceding_text);
          ic_info->preceding_text = mt;
        }
+      else
+       M17N_OBJECT_UNREF (mt);
     }
   else
     ic_info->preceding_text = mt;
@@ -542,12 +632,12 @@ get_following_char (MInputContext *ic, int pos)
   if (ic_info->following_text)
     {
       len = mtext_nchars (ic_info->following_text);
-      if (pos <= len)
-       return mtext_ref_char (ic_info->following_text, pos - 1);
+      if (pos < len)
+       return mtext_ref_char (ic_info->following_text, pos);
     }
-  mt = get_surrounding_text (ic, pos);
+  mt = get_surrounding_text (ic, pos + 1);
   if (! mt)
-    return -1;
+    return -2;
   len = mtext_nchars (mt);
   if (ic_info->following_text)
     {
@@ -556,69 +646,91 @@ get_following_char (MInputContext *ic, int pos)
          M17N_OBJECT_UNREF (ic_info->following_text);
          ic_info->following_text = mt;
        }
+      else
+       M17N_OBJECT_UNREF (mt);
     }
   else
     ic_info->following_text = mt;
-  if (pos > len)
+  if (pos >= len)
     return -1;
-  return mtext_ref_char (ic_info->following_text, pos - 1);
+  return mtext_ref_char (ic_info->following_text, pos);
 }
 
 static int
-surrounding_pos (MSymbol sym)
+surrounding_pos (MSymbol sym, int *pos)
 {
   char *name;
 
   if (sym == Mnil)
     return 0;
   name = MSYMBOL_NAME (sym);
-  if ((name[1] == '-' || name[1] == '+')
-      && name[2] >= '1' && name[2] <= '9')
-    return (name[1] == '-' ? - atoi (name + 2) : atoi (name + 2));
+  if (name[0] == '@'
+      && (name[1] == '-' ? (name[2] >= '1' && name[2] <= '9')
+         : name[1] == '+' ? (name[2] >= '0' && name[2] <= '9')
+         : 0))
+    {
+      *pos = name[1] == '-' ? - atoi (name + 2) : atoi (name + 2);
+      return 1;
+    }
   return 0;
 }
 
 static int
-integer_value (MInputContext *ic, MPlist *arg, MPlist **value, int surrounding)
+integer_value (MInputContext *ic, MPlist *arg, int surrounding)
 {
   MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
-  int code;
+  int code, pos;
   MText *preedit = ic->preedit;
   int len = mtext_nchars (preedit);
 
-  if (value)
-    *value = NULL;
   if (MPLIST_INTEGER_P (arg))
     return MPLIST_INTEGER (arg);
-  if (surrounding
-      && (surrounding = surrounding_pos (MPLIST_SYMBOL (arg))) != 0)
-    return (surrounding < 0
-           ? get_preceding_char (ic, - surrounding)
-           : get_following_char (ic, surrounding));
-  code = marker_code (MPLIST_SYMBOL (arg));
+
+  code = marker_code (MPLIST_SYMBOL (arg), surrounding);
   if (code < 0)
     {
       MPlist *val = resolve_variable (ic_info, MPLIST_SYMBOL (arg));
 
-      if (value)
-       *value = val;
       return (MPLIST_INTEGER_P (val) ? MPLIST_INTEGER (val) : 0);
     }
   if (code == '@')
     return ic_info->key_head;
-  if (code >= '0' && code <= '9')
-    code -= '0';
+  if ((code == '-' || code == '+'))
+    {
+      char *name = MSYMBOL_NAME (MPLIST_SYMBOL (arg));
+
+      if (name[2])
+       {
+         pos = atoi (name + 1);
+         if (pos == 0 && code == '-')
+           return get_preceding_char (ic, 0);
+         pos = ic->cursor_pos + pos;
+         if (pos < 0)
+           {
+             if (ic->produced && mtext_len (ic->produced) + pos >= 0)
+               return mtext_ref_char (ic->produced,
+                                      mtext_len (ic->produced) + pos);
+             return get_preceding_char (ic, - pos);
+           }
+         else if (pos >= len)
+           return get_following_char (ic, pos - len);
+       }
+      else
+       pos = ic->cursor_pos + (code == '+' ? 1 : -1);
+    }
+  else if (code >= '0' && code <= '9')
+    pos = code - '0';
   else if (code == '=')
-    code = ic->cursor_pos;
-  else if (code == '-' || code == '[')
-    code = ic->cursor_pos - 1;
-  else if (code == '+' || code == ']')
-    code = ic->cursor_pos + 1;
+    pos = ic->cursor_pos;
+  else if (code == '[')
+    pos = ic->cursor_pos - 1;
+  else if (code == ']')
+    pos = ic->cursor_pos + 1;
   else if (code == '<')
-    code = 0;
+    pos = 0;
   else if (code == '>')
-    code = len;
-  return (code >= 0 && code < len ? mtext_ref_char (preedit, code) : -1);
+    pos = len - 1;
+  return (pos >= 0 && pos < len ? mtext_ref_char (preedit, pos) : -1);
 }
 
 static int
@@ -632,7 +744,7 @@ parse_expression (MPlist *plist)
     return -1;
   plist = MPLIST_PLIST (plist);
   op = MPLIST_SYMBOL (plist);
-  if (op != Mplus && op != Mminus && op != Mstar && op != Mslush
+  if (op != Mplus && op != Mminus && op != Mstar && op != Mslash
       && op != Mand && op != Mor && op != Mnot
       && op != Mless && op != Mgreater && op != Mequal
       && op != Mless_equal && op != Mgreater_equal)
@@ -652,7 +764,7 @@ resolve_expression (MInputContext *ic, MPlist *plist)
   if (MPLIST_INTEGER_P (plist))
     return MPLIST_INTEGER (plist);
   if (MPLIST_SYMBOL_P (plist))
-    return integer_value (ic, plist, NULL, 1);
+    return integer_value (ic, plist, 1);
   if (! MPLIST_PLIST_P (plist))
     return 0;
   plist = MPLIST_PLIST (plist);
@@ -670,7 +782,7 @@ resolve_expression (MInputContext *ic, MPlist *plist)
   else if (op == Mstar)
     MPLIST_DO (plist, MPLIST_NEXT (plist))
       val *= resolve_expression (ic, plist);
-  else if (op == Mslush)
+  else if (op == Mslash)
     MPLIST_DO (plist, MPLIST_NEXT (plist))
       val /= resolve_expression (ic, plist);
   else if (op == Mand)
@@ -750,6 +862,11 @@ parse_action_list (MPlist *plist, MPlist *macros)
 
          pl = MPLIST_NEXT (pl);
 
+         if (action_name == M_candidates)
+           {
+             /* This is an already regularised action.  */
+             continue;
+           }
          if (action_name == Minsert)
            {
              if (MPLIST_MTEXT_P (pl))
@@ -757,9 +874,16 @@ parse_action_list (MPlist *plist, MPlist *macros)
                  if (mtext_nchars (MPLIST_MTEXT (pl)) == 0)
                    MERROR (MERROR_IM, -1);
                }
+             else if (MPLIST_INTEGER_P (pl))
+               {
+                 int c = MPLIST_INTEGER (pl);
+
+                 if (c < 0 || c > MCHAR_MAX)
+                   MERROR (MERROR_IM, -1);
+               }
              else if (MPLIST_PLIST_P (pl))
                {
-                 MPLIST_DO (pl, pl)
+                 MPLIST_DO (pl, MPLIST_PLIST (pl))
                    {
                      if (MPLIST_PLIST_P (pl))
                        {
@@ -800,8 +924,7 @@ parse_action_list (MPlist *plist, MPlist *macros)
              if (! MPLIST_TAIL_P (pl))
                {
                  if (! MPLIST_SYMBOL_P (pl)
-                     && (! MPLIST_INTEGER_P (pl)
-                         || MPLIST_INTEGER (pl) == 0))
+                     && ! MPLIST_INTEGER_P (pl))
                    MERROR (MERROR_IM, -1);                 
                }
            }
@@ -852,7 +975,8 @@ parse_action_list (MPlist *plist, MPlist *macros)
                MERROR (MERROR_IM, -1);
            }
          else if (action_name == Mshow || action_name == Mhide
-                  || action_name == Mcommit || action_name == Munhandle)
+                  || action_name == Mcommit || action_name == Munhandle
+                  || action_name == Mpop)
            ;
          else if (action_name == Mcond)
            {
@@ -863,7 +987,7 @@ parse_action_list (MPlist *plist, MPlist *macros)
          else if (! macros || ! mplist_get (macros, action_name))
            MERROR (MERROR_IM, -1);
        }
-      else
+      else if (! MPLIST_SYMBOL_P (plist))
        MERROR (MERROR_IM, -1);
     }
 
@@ -871,24 +995,16 @@ parse_action_list (MPlist *plist, MPlist *macros)
 }
 
 static MPlist *
-resolve_command (MSymbol language, MSymbol name, MSymbol command)
+resolve_command (MPlist *cmds, MSymbol command)
 {
-  MPlist *plist = get_nested_list (language, name, Mnil, M_command);
+  MPlist *plist;
 
-  if (! plist)
-    MERROR (MERROR_IM, NULL);
-  MPLIST_DO (plist, plist)
-    {
-      if (MPLIST_SYMBOL (plist) == command)
-       break;
-      plist = MPLIST_NEXT (plist);
-    }
-  if (MPLIST_TAIL_P (plist))
-    MERROR (MERROR_IM, NULL);
-  plist = MPLIST_NEXT (plist);  
-  if (! MPLIST_PLIST_P (plist))
-    MERROR (MERROR_IM, NULL);
-  plist = MPLIST_NEXT (MPLIST_PLIST (plist));
+  if (! cmds || ! (plist = mplist__assq (cmds, command)))
+    return NULL;
+  plist = MPLIST_PLIST (plist);        /* (NAME DESC STATUS [KEYSEQ ...]) */
+  plist = MPLIST_NEXT (plist);
+  plist = MPLIST_NEXT (plist);
+  plist = MPLIST_NEXT (plist);
   return plist;
 }
 
@@ -908,19 +1024,22 @@ load_translation (MIMMap *map, MPlist *keylist, MPlist *map_actions,
       MText *mt = MPLIST_MTEXT (keylist);
 
       len = mtext_nchars (mt);
-      if (len == 0 || len != mtext_nbytes (mt))
-       MERROR (MERROR_IM, -1);
+      if (MFAILP (len > 0 && len == mtext_nbytes (mt)))
+       return -1;
       keyseq = (MSymbol *) alloca (sizeof (MSymbol) * len);
       for (i = 0; i < len; i++)
        keyseq[i] = one_char_symbol[MTEXT_DATA (mt)[i]];
     }
-  else if (MPLIST_PLIST_P (keylist))
+  else
     {
-      MPlist *elt = MPLIST_PLIST (keylist);
+      MPlist *elt;
          
+      if (MFAILP (MPLIST_PLIST_P (keylist)))
+       return -1;
+      elt = MPLIST_PLIST (keylist);
       len = MPLIST_LENGTH (elt);
-      if (len == 0)
-       MERROR (MERROR_IM, -1);
+      if (MFAILP (len > 0))
+       return -1;
       keyseq = (MSymbol *) alloca (sizeof (int) * len);
       for (i = 0; i < len; i++, elt = MPLIST_NEXT (elt))
        {
@@ -928,18 +1047,18 @@ load_translation (MIMMap *map, MPlist *keylist, MPlist *map_actions,
            {
              int c = MPLIST_INTEGER (elt);
 
-             if (c < 0 || c >= 0x100)
-               MERROR (MERROR_IM, -1);
+             if (MFAILP (c >= 0 && c < 0x100))
+               return -1;
              keyseq[i] = one_char_symbol[c];
            }
-         else if (MPLIST_SYMBOL_P (elt))
-           keyseq[i] = MPLIST_SYMBOL (elt);
          else
-           MERROR (MERROR_IM, -1);
+           {
+             if (MFAILP (MPLIST_SYMBOL_P (elt)))
+               return -1;
+             keyseq[i] = MPLIST_SYMBOL (elt);
+           }
        }
     }
-  else
-    MERROR (MERROR_IM, -1);
 
   for (i = 0; i < len; i++)
     {
@@ -980,25 +1099,22 @@ load_translation (MIMMap *map, MPlist *keylist, MPlist *map_actions,
 }
 
 /* Load a branch from PLIST into MAP.  PLIST has this form:
-      PLIST ::= ( MAP-NAME BRANCH-ACTION * )
-   MAPS is a plist of raw maps.
-   STATE is the current state.  */
+      PLIST ::= ( MAP-NAME BRANCH-ACTION * )  */
 
 static int
-load_branch (MPlist *plist, MPlist *maps, MIMMap *map,
-            MSymbol language, MSymbol name, MPlist *macros)
+load_branch (MInputMethodInfo *im_info, MPlist *plist, MIMMap *map)
 {
   MSymbol map_name;
   MPlist *branch_actions;
 
-  if (! MPLIST_SYMBOL_P (plist))
-    MERROR (MERROR_IM, -1);
+  if (MFAILP (MPLIST_SYMBOL_P (plist)))
+    return -1;
   map_name = MPLIST_SYMBOL (plist);
   plist = MPLIST_NEXT (plist);
   if (MPLIST_TAIL_P (plist))
     branch_actions = NULL;
-  else if (parse_action_list (plist, macros) < 0)
-    MERROR (MERROR_IM, -1);
+  else if (MFAILP (parse_action_list (plist, im_info->macros) >= 0))
+    return -1;
   else
     branch_actions = plist;
   if (map_name == Mnil)
@@ -1013,44 +1129,61 @@ load_branch (MPlist *plist, MPlist *maps, MIMMap *map,
       if (branch_actions)
        M17N_OBJECT_REF (branch_actions);
     }
-  else if (maps && (plist = (MPlist *) mplist_get (maps, map_name)))
+  else if (im_info->maps) 
     {
-      MPLIST_DO (plist, plist)
+      plist = (MPlist *) mplist_get (im_info->maps, map_name);
+      if (! plist && im_info->configured_vars)
        {
-         MPlist *keylist, *map_actions;
+         MPlist *p = mplist__assq (im_info->configured_vars, map_name);
 
-         if (! MPLIST_PLIST_P (plist))
-           MERROR (MERROR_IM, -1);
-         keylist = MPLIST_PLIST (plist);
-         map_actions = MPLIST_NEXT (keylist);
-         if (MPLIST_SYMBOL_P (keylist))
+         if (p && MPLIST_PLIST_P (p))
+           {
+             p = MPLIST_NEXT (MPLIST_NEXT (MPLIST_NEXT (MPLIST_PLIST (p))));
+             if (MPLIST_SYMBOL_P (p))
+               plist = mplist_get (im_info->maps, MPLIST_SYMBOL (p));
+           }
+       }
+      if (plist)
+       {
+         MPLIST_DO (plist, plist)
            {
-             MSymbol command = MPLIST_SYMBOL (keylist);
-             MPlist *pl = resolve_command (language, name, command);
+             MPlist *keylist, *map_actions;
 
-             if (! pl)
-               return -1;
-             MPLIST_DO (pl, pl)
-               if (load_translation (map, pl, map_actions, branch_actions,
-                                     macros) < 0)
-                 MERROR (MERROR_IM, -1);
+             if (! MPLIST_PLIST_P (plist))
+               MERROR (MERROR_IM, -1);
+             keylist = MPLIST_PLIST (plist);
+             map_actions = MPLIST_NEXT (keylist);
+             if (MPLIST_SYMBOL_P (keylist))
+               {
+                 MSymbol command = MPLIST_SYMBOL (keylist);
+                 MPlist *pl;
+
+                 if (MFAILP (command != Mat_reload))
+                   continue;
+                 pl = resolve_command (im_info->configured_cmds, command);
+                 if (MFAILP (pl))
+                   continue;
+                 MPLIST_DO (pl, pl)
+                   load_translation (map, pl, map_actions, branch_actions,
+                                     im_info->macros);
+               }
+             else
+               load_translation (map, keylist, map_actions, branch_actions,
+                                 im_info->macros);
            }
-         else
-           if (load_translation (map, keylist, map_actions, branch_actions,
-                                 macros) < 0)
-             MERROR (MERROR_IM, -1);
        }
     }
 
   return 0;
 }
 
-/* Load a macro from PLIST into MACROS.
-   PLIST has this from:
+/* Load a macro from PLIST into IM_INFO->macros.
+   PLIST has this form:
       PLIST ::= ( MACRO-NAME ACTION * )
-   MACROS is a plist of macro names vs action list.  */
+   IM_INFO->macros is a plist of macro names vs action list.  */
+
 static int
-load_macros (MPlist *plist, MPlist *macros)
+load_macros (MInputMethodInfo *im_info, MPlist *plist)
 {
   MSymbol name; 
   MPlist *pl;
@@ -1059,24 +1192,26 @@ load_macros (MPlist *plist, MPlist *macros)
     MERROR (MERROR_IM, -1);
   name = MPLIST_SYMBOL (plist);
   plist = MPLIST_NEXT (plist);
-  if (MPLIST_TAIL_P (plist)
-      || parse_action_list (plist, macros) < 0)
+  if (MFAILP (! MPLIST_TAIL_P (plist)))
     MERROR (MERROR_IM, -1);
-  pl = mplist_get (macros, name);
-  if (pl)
-    M17N_OBJECT_UNREF (pl);
-  mplist_put (macros, name, plist);
+  pl = mplist_get (im_info->macros, name);
+  M17N_OBJECT_UNREF (pl);
+  mplist_put (im_info->macros, name, plist);
   M17N_OBJECT_REF (plist);
   return 0;
 }
 
-/* Load an external module from PLIST into EXTERNALS.
+/* Load an external module from PLIST, and return a pointer to
+   MIMExternalModule.
+
    PLIST has this form:
       PLIST ::= ( MODULE-NAME FUNCTION * )
-   EXTERNALS is a plist of MODULE-NAME vs (MIMExternalModule *).  */
+   IM_INFO->externals is a plist of MODULE-NAME vs (MIMExternalModule *).
 
-static int
-load_external_module (MPlist *plist, MPlist *externals)
+   On error, return NULL.  */
+
+static MIMExternalModule *
+load_external_module (MPlist *plist)
 {
   void *handle;
   MSymbol module;
@@ -1089,37 +1224,44 @@ load_external_module (MPlist *plist, MPlist *externals)
     module = msymbol ((char *) MTEXT_DATA (MPLIST_MTEXT (plist)));
   else if (MPLIST_SYMBOL_P (plist))
     module = MPLIST_SYMBOL (plist);
-  module_file = alloca (strlen (MSYMBOL_NAME (module))
+  module_file = alloca (strlen (M17N_MODULE_DIR) + 1
+                       + strlen (MSYMBOL_NAME (module))
                        + strlen (DLOPEN_SHLIB_EXT) + 1);
-  sprintf (module_file, "%s%s", MSYMBOL_NAME (module), DLOPEN_SHLIB_EXT);
+  sprintf (module_file, "%s/%s%s",
+          M17N_MODULE_DIR, MSYMBOL_NAME (module), DLOPEN_SHLIB_EXT);
 
   handle = dlopen (module_file, RTLD_NOW);
-  if (! handle)
-    {
-      fprintf (stderr, "%s\n", dlerror ());
-      MERROR (MERROR_IM, -1);
-    }
+  if (MFAILP (handle))
+    return NULL;
   func_list = mplist ();
   MPLIST_DO (plist, MPLIST_NEXT (plist))
     {
       if (! MPLIST_SYMBOL_P (plist))
        MERROR_GOTO (MERROR_IM, err_label);
       func = dlsym (handle, MSYMBOL_NAME (MPLIST_SYMBOL (plist)));
-      if (! func)
-       MERROR_GOTO (MERROR_IM, err_label);
+      if (MFAILP (func))
+       goto err_label;
       mplist_add (func_list, MPLIST_SYMBOL (plist), func);
     }
 
   MSTRUCT_MALLOC (external, MERROR_IM);
+  external->name = module;
   external->handle = handle;
   external->func_list = func_list;
-  mplist_add (externals, module, external);
-  return 0;
+  return external;
 
  err_label:
-  dlclose (handle);
   M17N_OBJECT_UNREF (func_list);
-  return -1;
+  dlclose (handle);
+  return NULL;
+}
+
+static void
+unload_external_module (MIMExternalModule *external)
+{
+  dlclose (external->handle);
+  M17N_OBJECT_UNREF (external->func_list);
+  free (external);
 }
 
 static void
@@ -1144,8 +1286,7 @@ free_state (void *object)
 {
   MIMState *state = object;
 
-  if (state->title)
-    M17N_OBJECT_UNREF (state->title);
+  M17N_OBJECT_UNREF (state->title);
   if (state->map)
     free_map (state->map, 1);
   free (state);
@@ -1155,17 +1296,15 @@ free_state (void *object)
     PLIST has this form:
       PLIST ::= ( STATE-NAME STATE-TITLE ? BRANCH * )
       BRANCH ::= ( MAP-NAME BRANCH-ACTION * )
-   MAPS is a plist of defined maps.
    Return the state object.  */
 
 static MIMState *
-load_state (MPlist *plist, MPlist *maps, MSymbol language, MSymbol name,
-           MPlist *macros)
+load_state (MInputMethodInfo *im_info, MPlist *plist)
 {
   MIMState *state;
 
-  if (! MPLIST_SYMBOL_P (plist))
-    MERROR (MERROR_IM, NULL);
+  if (MFAILP (MPLIST_SYMBOL_P (plist)))
+    return NULL;
   M17N_OBJECT (state, free_state, MERROR_IM);
   state->name = MPLIST_SYMBOL (plist);
   plist = MPLIST_NEXT (plist);
@@ -1173,29 +1312,62 @@ load_state (MPlist *plist, MPlist *maps, MSymbol language, MSymbol name,
     {
       state->title = MPLIST_MTEXT (plist);
       mtext_put_prop (state->title, 0, mtext_nchars (state->title),
-                     Mlanguage, language);
+                     Mlanguage, im_info->language);
       M17N_OBJECT_REF (state->title);
       plist = MPLIST_NEXT (plist);
     }
   MSTRUCT_CALLOC (state->map, MERROR_IM);
   MPLIST_DO (plist, plist)
-    if (! MPLIST_PLIST_P (plist)
-       || load_branch (MPLIST_PLIST (plist), maps, state->map, language, name,
-                       macros) < 0)
-      MERROR (MERROR_IM, NULL);
+    {
+      if (MFAILP (MPLIST_PLIST_P (plist)))
+       continue;
+      load_branch (im_info, MPLIST_PLIST (plist), state->map);
+    }
   return state;
 }
 
+/* Return a newly created IM_INFO for an input method specified by
+   LANUAGE, NAME, and EXTRA.  IM_INFO is stored in PLIST.  */
 
-static MPlist *im_info_list;
+static MInputMethodInfo *
+new_im_info (MDatabase *mdb, MSymbol language, MSymbol name, MSymbol extra,
+            MPlist *plist)
+{
+  MInputMethodInfo *im_info;
+  MPlist *elt;
+
+  if (name == Mnil && extra == Mnil)
+    language = Mt, extra = Mglobal;
+  MSTRUCT_CALLOC (im_info, MERROR_IM);
+  im_info->mdb = mdb;
+  im_info->language = language;
+  im_info->name = name;
+  im_info->extra = extra;
+
+  elt = mplist ();
+  mplist_add (plist, Mplist, elt);
+  M17N_OBJECT_UNREF (elt);
+  elt = mplist_add (elt, Msymbol, language);
+  elt = mplist_add (elt, Msymbol, name);
+  elt = mplist_add (elt, Msymbol, extra);
+  mplist_add (elt, Mt, im_info);
+
+  return im_info;
+}
 
 static void
-free_im_info (MInputMethodInfo *im_info)
+fini_im_info (MInputMethodInfo *im_info)
 {
   MPlist *plist;
 
-  if (im_info->title)
-    M17N_OBJECT_UNREF (im_info->title);
+  M17N_OBJECT_UNREF (im_info->cmds);
+  M17N_OBJECT_UNREF (im_info->configured_cmds);
+  M17N_OBJECT_UNREF (im_info->bc_cmds);
+  M17N_OBJECT_UNREF (im_info->vars);
+  M17N_OBJECT_UNREF (im_info->configured_vars);
+  M17N_OBJECT_UNREF (im_info->bc_vars);
+  M17N_OBJECT_UNREF (im_info->description);
+  M17N_OBJECT_UNREF (im_info->title);
   if (im_info->states)
     {
       MPLIST_DO (plist, im_info->states)
@@ -1218,11 +1390,7 @@ free_im_info (MInputMethodInfo *im_info)
     {
       MPLIST_DO (plist, im_info->externals)
        {
-         MIMExternalModule *external = MPLIST_VAL (plist);
-
-         dlclose (external->handle);
-         M17N_OBJECT_UNREF (external->func_list);
-         free (external);
+         unload_external_module (MPLIST_VAL (plist));
          MPLIST_KEY (plist) = Mt;
        }
       M17N_OBJECT_UNREF (im_info->externals);
@@ -1238,2681 +1406,4883 @@ free_im_info (MInputMethodInfo *im_info)
       M17N_OBJECT_UNREF (im_info->maps);
     }
 
-  free (im_info);
+  im_info->tick = 0;
 }
 
-static MInputMethodInfo *get_im_info (MSymbol language, MSymbol name,
-                                     MSymbol extra);
+static void
+free_im_info (MInputMethodInfo *im_info)
+{
+  fini_im_info (im_info);
+  free (im_info);
+}
 
-static MInputMethodInfo *
-get_im_info_by_tags (MPlist *plist)
+static void
+free_im_list (MPlist *plist)
 {
-  MSymbol tag[3];
-  int i;
+  MPlist *pl, *elt;
 
-  for (i = 0; i < 3 && MPLIST_SYMBOL_P (plist);
-       i++, plist = MPLIST_NEXT (plist))
-    tag[i] = MPLIST_SYMBOL (plist);
-  if (i < 2)
-    return NULL;
-  for (; i < 3; i++)
-    tag[i] = Mnil;
-  return get_im_info (tag[0], tag[1], tag[2]);
-}
+  MPLIST_DO (pl, plist)
+    {
+      MInputMethodInfo *im_info;
 
-/* Load an input method from PLIST into IM_INTO, and return it.  */
+      elt = MPLIST_NEXT (MPLIST_NEXT (MPLIST_NEXT (MPLIST_PLIST (pl))));
+      im_info = MPLIST_VAL (elt);
+      free_im_info (im_info);
+    }
+  M17N_OBJECT_UNREF (plist);
+}
 
 static MInputMethodInfo *
-load_im_info (MSymbol language, MSymbol name, MPlist *plist)
+lookup_im_info (MPlist *plist, MSymbol language, MSymbol name, MSymbol extra)
 {
-  MInputMethodInfo *im_info;
-  MText *title = NULL;
-  MPlist *maps = NULL;
-  MPlist *states = NULL;
-  MPlist *externals = NULL;
-  MPlist *macros = NULL;
-  MPlist *elt;
-
-  MSTRUCT_CALLOC (im_info, MERROR_IM);
-
-  while (MPLIST_PLIST_P (plist))
+  if (name == Mnil && extra == Mnil)
+    language = Mt, extra = Mglobal;
+  while ((plist = mplist__assq (plist, language)))
     {
-      elt = MPLIST_PLIST (plist);
-      if (! MPLIST_SYMBOL_P (elt))
-       MERROR_GOTO (MERROR_IM, err);
-      if (MPLIST_SYMBOL (elt) == Mtitle)
-       {
-         elt = MPLIST_NEXT (elt);
-         if (! MPLIST_MTEXT_P (elt))
-           MERROR_GOTO (MERROR_IM, err);
-         im_info->title = title = MPLIST_MTEXT (elt);
-         M17N_OBJECT_REF (title);
-       }
-      else if (MPLIST_SYMBOL (elt) == Mmap)
-       {
-         MPlist *pl = mplist__from_alist (MPLIST_NEXT (elt));
-
-         if (! pl)
-           MERROR_GOTO (MERROR_IM, err);
-         if (! maps)
-           im_info->maps = maps = pl;
-         else
-           maps = mplist__conc (maps, pl);
-       }
-      else if (MPLIST_SYMBOL (elt) == Mmacro)
-       {
-         if (! macros)
-           im_info->macros = macros = mplist ();
-         MPLIST_DO (elt, MPLIST_NEXT (elt))
-           {
-             if (! MPLIST_PLIST_P (elt)
-                 || load_macros (MPLIST_PLIST (elt), macros) < 0)
-               MERROR_GOTO (MERROR_IM, err);
-           }
-       }
-      else if (MPLIST_SYMBOL (elt) == Mmodule)
-       {
-         if (! externals)
-           im_info->externals = externals = mplist ();
-         MPLIST_DO (elt, MPLIST_NEXT (elt))
-           {
-             if (! MPLIST_PLIST_P (elt)
-                 || load_external_module (MPLIST_PLIST (elt), externals) < 0)
-               MERROR_GOTO (MERROR_IM, err);
-           }
-       }
-      else if (MPLIST_SYMBOL (elt) == Mstate)
-       {
-         MPLIST_DO (elt, MPLIST_NEXT (elt))
-           {
-             MIMState *state;
-
-             if (! MPLIST_PLIST_P (elt))
-               MERROR_GOTO (MERROR_IM, err);
-             state = load_state (MPLIST_PLIST (elt), maps, language, name,
-                                 macros);
-             if (! state)
-               MERROR_GOTO (MERROR_IM, err);
-             if (! states)
-               im_info->states = states = mplist ();
-             mplist_put (states, state->name, state);
-           }
-       }
-      else if (MPLIST_SYMBOL (elt) == Minclude)
-       {
-         /* elt ::= include (tag1 tag2 ...) key item ... */
-         MSymbol key;
-         MInputMethodInfo *temp;
-         MPlist *pl, *p;
-
-         elt = MPLIST_NEXT (elt);
-         if (! MPLIST_PLIST_P (elt))
-           MERROR_GOTO (MERROR_IM, err);
-         temp = get_im_info_by_tags (MPLIST_PLIST (elt));
-         if (! temp)
-           MERROR_GOTO (MERROR_IM, err);
-         elt = MPLIST_NEXT (elt);
-         if (! MPLIST_SYMBOL_P (elt))
-           MERROR_GOTO (MERROR_IM, err);
-         key = MPLIST_SYMBOL (elt);
-         elt = MPLIST_NEXT (elt);
-         if (key == Mmap)
-           {
-             if (! maps)
-               im_info->maps = maps = mplist ();
-             MPLIST_DO (pl, temp->maps)
-               {
-                 p = MPLIST_VAL (pl);
-                 MPLIST_ADD_PLIST (maps, MPLIST_KEY (pl), p);
-                 M17N_OBJECT_REF (p);
-               }
-           }
-         else if (key == Mmacro)
-           {
-             if (! macros)
-               im_info->macros = macros = mplist ();
-             MPLIST_DO (pl, temp->macros)
-               {
-                 p = MPLIST_VAL (pl);
-                 MPLIST_ADD_PLIST (macros, MPLIST_KEY (pl), p);
-                 M17N_OBJECT_REF (p);
-               }
-           }
-         else if (key == Mstate)
-           {
-             if (! states)
-               im_info->states = states = mplist ();
-             MPLIST_DO (pl, temp->states)
-               {
-                 MIMState *state = MPLIST_VAL (pl);
+      MPlist *elt = MPLIST_PLIST (plist);
 
-                 mplist_add (states, MPLIST_KEY (pl), state);
-                 M17N_OBJECT_REF (state);
-               }
-           }
-         else
-           MERROR_GOTO (MERROR_IM, err);
-       }
       plist = MPLIST_NEXT (plist);
+      elt = MPLIST_NEXT (elt);
+      if (MPLIST_SYMBOL (elt) != name)
+       continue;
+      elt = MPLIST_NEXT (elt);
+      if (MPLIST_SYMBOL (elt) != extra)
+       continue;
+      elt = MPLIST_NEXT (elt);
+      return MPLIST_VAL (elt);
     }
-
-  if (! states)
-    goto err;
-  if (! title && name)
-    im_info->title
-      = title = mtext_from_data (MSYMBOL_NAME (name), MSYMBOL_NAMELEN (name),
-                                MTEXT_FORMAT_US_ASCII);
-  return im_info;
-
- err:
-  free_im_info (im_info);
   return NULL;
 }
 
-\f
+static void load_im_info (MPlist *, MInputMethodInfo *);
 
-static int take_action_list (MInputContext *ic, MPlist *action_list);
-static void preedit_commit (MInputContext *ic);
+#define get_custom_info(im_info)                               \
+  (im_custom_list                                              \
+   ? lookup_im_info (im_custom_list, (im_info)->language,      \
+                    (im_info)->name, (im_info)->extra)         \
+   : NULL)
 
-static void
-shift_state (MInputContext *ic, MSymbol state_name)
+#define get_config_info(im_info)                               \
+  (im_config_list                                              \
+   ? lookup_im_info (im_config_list, (im_info)->language,      \
+                    (im_info)->name, (im_info)->extra)         \
+   : NULL)
+
+static int
+update_custom_info (void)
 {
-  MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
-  MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
-  MIMState *orig_state = ic_info->state, *state;
+  MPlist *plist, *pl;
 
-  /* Find a state to shift to.  If not found, shift to the initial
-     state.  */
-  if (state_name == Mt)
+  if (im_custom_mdb)
     {
-      if (! ic_info->prev_state)
-       return;
-      state = ic_info->prev_state;
+      if (mdatabase__check (im_custom_mdb) > 0)
+       return 1;
     }
   else
     {
-      state = (MIMState *) mplist_get (im_info->states, state_name);
-      if (! state)
-       state = (MIMState *) MPLIST_VAL (im_info->states);
+      MDatabaseInfo *custom_dir_info;
+      char custom_path[PATH_MAX + 1];
+
+      custom_dir_info = MPLIST_VAL (mdatabase__dir_list);
+      if (! custom_dir_info->filename
+         || custom_dir_info->len + strlen (CUSTOM_FILE) > PATH_MAX)
+       return -1;
+      strcpy (custom_path, custom_dir_info->filename);
+      strcat (custom_path, CUSTOM_FILE);
+      im_custom_mdb = mdatabase_define (Minput_method, Mt, Mnil, Mconfig,
+                                       NULL, custom_path);
     }
 
-  MDEBUG_PRINT1 ("\n  [IM] (shift %s)", MSYMBOL_NAME (state->name));
+  if (im_custom_list)
+    {
+      free_im_list (im_custom_list);
+      im_custom_list = NULL;
+    }
+  plist = mdatabase_load (im_custom_mdb);
+  if (! plist)
+    return -1;
+  im_custom_list = mplist ();
 
-  /* Enter the new state.  */
-  ic_info->state = state;
-  ic_info->map = state->map;
-  ic_info->state_key_head = ic_info->key_head;
-  if (state == (MIMState *) MPLIST_VAL (im_info->states))
-    /* We have shifted to the initial state.  */
-    preedit_commit (ic);
-  mtext_cpy (ic_info->preedit_saved, ic->preedit);
-  ic_info->state_pos = ic->cursor_pos;
-  if (state != orig_state )
+  MPLIST_DO (pl, plist)
     {
-      if (state == (MIMState *) MPLIST_VAL (im_info->states))
-       ic_info->prev_state = NULL;
-      else
-       ic_info->prev_state = orig_state;
+      MSymbol language, name, extra;
+      MInputMethodInfo *im_info;
+      MPlist *im_data, *p;
 
-      if (state->title)
-       ic->status = state->title;
-      else
-       ic->status = im_info->title;
-      ic->status_changed = 1;
-      if (ic_info->map == ic_info->state->map
-         && ic_info->map->map_actions)
-       {
-         MDEBUG_PRINT (" init-actions:");
-         take_action_list (ic, ic_info->map->map_actions);
-       }
+      if (! MPLIST_PLIST_P (pl))
+       continue;
+      p = MPLIST_PLIST (pl);
+      im_data = MPLIST_NEXT (p);
+      if (! MPLIST_PLIST_P (p))
+       continue;
+      p = MPLIST_PLIST (p);
+      if (! MPLIST_SYMBOL_P (p)
+         || MPLIST_SYMBOL (p) != Minput_method)
+       continue;
+      p = MPLIST_NEXT (p);
+      if (! MPLIST_SYMBOL_P (p))
+       continue;
+      language = MPLIST_SYMBOL (p);
+      p = MPLIST_NEXT (p);
+      if (! MPLIST_SYMBOL_P (p))
+       continue;
+      name = MPLIST_SYMBOL (p);
+      p = MPLIST_NEXT (p);
+      if (MPLIST_TAIL_P (p))
+       extra = Mnil;
+      else if (MPLIST_SYMBOL_P (p))
+       extra = MPLIST_SYMBOL (p);
+      if (language == Mnil || (name == Mnil && extra == Mnil))
+       continue;
+      im_info = new_im_info (NULL, language, name, extra, im_custom_list);
+      load_im_info (im_data, im_info);
     }
+  M17N_OBJECT_UNREF (plist);
+  return 0;
 }
 
-/* Find a candidate group that contains a candidate number INDEX from
-   PLIST.  Set START_INDEX to the first candidate number of the group,
-   END_INDEX to the last candidate number plus 1, GROUP_INDEX to the
-   candidate group number if they are non-NULL.  If INDEX is -1, find
-   the last candidate group.  */
-
-static MPlist *
-find_candidates_group (MPlist *plist, int index,
-                      int *start_index, int *end_index, int *group_index)
+static int
+update_global_info (void)
 {
-  int i = 0, gidx = 0, len;
+  MPlist *plist;
 
-  MPLIST_DO (plist, plist)
+  if (global_info)
     {
-      if (MPLIST_MTEXT_P (plist))
-       len = mtext_nchars (MPLIST_MTEXT (plist));
-      else
-       len = mplist_length (MPLIST_PLIST (plist));
-      if (index < 0 ? MPLIST_TAIL_P (MPLIST_NEXT (plist))
-         : i + len > index)
-       {
-         if (start_index)
-           *start_index = i;
-         if (end_index)
-           *end_index = i + len;
-         if (group_index)
-           *group_index = gidx;
-         return plist;
-       }
-      i += len;
-      gidx++;
+      int ret = mdatabase__check (global_info->mdb);
+
+      if (ret)
+       return ret;
+      fini_im_info (global_info);
     }
-  return NULL;
+  else
+    {
+      MDatabase *mdb = mdatabase_find (Minput_method, Mt, Mnil, Mglobal);
+
+      if (! mdb)
+       return -1;
+      global_info = new_im_info (mdb, Mt, Mnil, Mglobal, im_info_list);
+    }
+  if (! global_info->mdb
+      || ! (plist = mdatabase_load (global_info->mdb)))
+    return -1;
+
+  load_im_info (plist, global_info);
+  M17N_OBJECT_UNREF (plist);
+  return 0;
 }
 
-static void
-preedit_insert (MInputContext *ic, int pos, MText *mt, int c)
+
+/* Return an IM_INFO for the input method specified by LANGUAGE, NAME,
+   and EXTRA.  KEY, if not Mnil, tells which kind of information about
+   the input method is necessary, and the returned IM_INFO may contain
+   only that information.  */
+
+static MInputMethodInfo *
+get_im_info (MSymbol language, MSymbol name, MSymbol extra, MSymbol key)
 {
-  MInputContextInfo *ic_info = ((MInputContext *) ic)->info;
-  MPlist *markers;
-  int nchars = mt ? mtext_nchars (mt) : 1;
+  MPlist *plist;
+  MInputMethodInfo *im_info;
+  MDatabase *mdb;
 
-  if (mt)
-    mtext_ins (ic->preedit, pos, mt);
+  if (name == Mnil && extra == Mnil)
+    language = Mt, extra = Mglobal;
+  im_info = lookup_im_info (im_info_list, language, name, extra);
+  if (im_info)
+    {
+      if (key == Mnil ? im_info->states != NULL
+         : key == Mcommand ? im_info->cmds != NULL
+         : key == Mvariable ? im_info->vars != NULL
+         : key == Mtitle ? im_info->title != NULL
+         : key == Mdescription ? im_info->description != NULL
+         : 1)
+       /* IM_INFO already contains required information.  */
+       return im_info;
+      /* We have not yet loaded required information.  */
+    }
   else
-    mtext_ins_char (ic->preedit, pos, c, 1);
-  MPLIST_DO (markers, ic_info->markers)
-    if (MPLIST_INTEGER (markers) > pos)
-      MPLIST_VAL (markers) = (void *) (MPLIST_INTEGER (markers) + nchars);
-  if (ic->cursor_pos >= pos)
-    ic->cursor_pos += nchars;
-  ic->preedit_changed = 1;
+    {
+      mdb = mdatabase_find (Minput_method, language, name, extra);
+      if (! mdb)
+       return NULL;
+      im_info = new_im_info (mdb, language, name, extra, im_info_list);
+    }
+
+  if (key == Mnil)
+    {
+      plist = mdatabase_load (im_info->mdb);
+    }
+  else
+    {
+      mplist_push (load_im_info_keys, key, Mt);
+      plist = mdatabase__load_for_keys (im_info->mdb, load_im_info_keys);
+      mplist_pop (load_im_info_keys);
+    }
+  im_info->tick = 0;
+  if (! plist)
+    MERROR (MERROR_IM, im_info);
+  update_global_info ();
+  load_im_info (plist, im_info);
+  M17N_OBJECT_UNREF (plist);
+  if (key == Mnil)
+    {
+      if (! im_info->cmds)
+       im_info->cmds = mplist ();
+      if (! im_info->vars)
+       im_info->vars = mplist ();
+      if (! im_info->states)
+       im_info->states = mplist ();
+    }
+  if (! im_info->title
+      && (key == Mnil || key == Mtitle))
+    im_info->title = (name == Mnil ? mtext ()
+                     : mtext_from_data (MSYMBOL_NAME (name),
+                                        MSYMBOL_NAMELEN (name),
+                                        MTEXT_FORMAT_US_ASCII));
+  return im_info;
 }
 
+/* Check if IM_INFO->mdb is updated or not.  If not updated, return 0.
+   If updated, but got unloadable, return -1.  Otherwise, update
+   contents of IM_INFO from the new database, and return 1.  */
 
-static void
-preedit_delete (MInputContext *ic, int from, int to)
+static int
+reload_im_info (MInputMethodInfo *im_info)
 {
-  MInputContextInfo *ic_info = ((MInputContext *) ic)->info;
-  MPlist *markers;
+  int check;
+  MPlist *plist;
 
-  mtext_del (ic->preedit, from, to);
-  MPLIST_DO (markers, ic_info->markers)
-    {
-      if (MPLIST_INTEGER (markers) > to)
-       MPLIST_VAL (markers)
-         = (void *) (MPLIST_INTEGER (markers) - (to - from));
-      else if (MPLIST_INTEGER (markers) > from);
-       MPLIST_VAL (markers) = (void *) from;
-    }
-  if (ic->cursor_pos >= to)
-    ic->cursor_pos -= to - from;
-  else if (ic->cursor_pos > from)
-    ic->cursor_pos = from;
-  ic->preedit_changed = 1;
+  update_custom_info ();
+  update_global_info ();
+  check = mdatabase__check (im_info->mdb);
+  if (check < 0)
+    return -1;
+  plist = mdatabase_load (im_info->mdb);
+  if (! plist)
+    return -1;
+  fini_im_info (im_info);
+  load_im_info (plist, im_info);
+  M17N_OBJECT_UNREF (plist);
+  if (! im_info->cmds)
+    im_info->cmds = mplist ();
+  if (! im_info->vars)
+    im_info->vars = mplist ();
+  if (! im_info->title)
+    {
+      MSymbol name = im_info->name;
+
+      im_info->title = (name == Mnil ? mtext ()
+                       : mtext_from_data (MSYMBOL_NAME (name),
+                                          MSYMBOL_NAMELEN (name),
+                                          MTEXT_FORMAT_US_ASCII));
+    }
+  return 1;
 }
 
-static void
-preedit_commit (MInputContext *ic)
+static MInputMethodInfo *
+get_im_info_by_tags (MPlist *plist)
 {
-  MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
-  int preedit_len = mtext_nchars (ic->preedit);
+  MSymbol tag[3];
+  int i;
 
-  if (preedit_len > 0)
-    {
-      MPlist *p;
+  for (i = 0; i < 3 && MPLIST_SYMBOL_P (plist);
+       i++, plist = MPLIST_NEXT (plist))
+    tag[i] = MPLIST_SYMBOL (plist);
+  if (i < 2)
+    return NULL;
+  for (; i < 3; i++)
+    tag[i] = Mnil;
+  return get_im_info (tag[0], tag[1], tag[2], Mnil);
+}
 
-      mtext_put_prop_values (ic->preedit, 0, mtext_nchars (ic->preedit),
-                            Mcandidate_list, NULL, 0);
-      mtext_put_prop_values (ic->preedit, 0, mtext_nchars (ic->preedit),
-                            Mcandidate_index, NULL, 0);
-      mtext_cat (ic->produced, ic->preedit);
-      if ((mdebug__flag & mdebug_mask)
-         && mtext_nchars (ic->preedit) > 0)
-       {
-         int i;
 
-         MDEBUG_PRINT (" (produced");
-         for (i = 0; i < mtext_nchars (ic->preedit); i++)
-           MDEBUG_PRINT1 (" U+%04X", mtext_ref_char (ic->preedit, i));
-         MDEBUG_PRINT (")");
-       }
-      mtext_reset (ic->preedit);
-      mtext_reset (ic_info->preedit_saved);
-      MPLIST_DO (p, ic_info->markers)
-       MPLIST_VAL (p) = 0;
-      ic->cursor_pos = ic_info->state_pos = 0;
-      ic->preedit_changed = 1;
-    }
-  if (ic->candidate_list)
+static int
+check_description (MPlist *plist)
+{
+  MText *mt;
+
+  if (MPLIST_MTEXT_P (plist))
+    return 1;
+  if (MPLIST_PLIST_P (plist))
     {
-      M17N_OBJECT_UNREF (ic->candidate_list);
-      ic->candidate_list = NULL;
-      ic->candidates_changed = MINPUT_CANDIDATES_LIST_CHANGED;
-      if (ic->candidate_show)
-       {
-         ic->candidate_show = 0;
-         ic->candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;
-       }
-    }
+      MPlist *pl = MPLIST_PLIST (plist);
+
+      if (MFAILP (MPLIST_SYMBOL_P (pl) && MPLIST_SYMBOL (pl) == M_gettext))
+       return 0;
+      pl =MPLIST_NEXT (pl);
+      if (MFAILP (MPLIST_MTEXT_P (pl)))
+       return 0;
+      mt = MPLIST_MTEXT (pl);
+      M17N_OBJECT_REF (mt);
+#if ENABLE_NLS
+      {
+       char *translated = dgettext ("m17n-db", (char *) MTEXT_DATA (mt));
 
-  memmove (ic_info->keys, ic_info->keys + ic_info->key_head,
-          sizeof (int) * (ic_info->used - ic_info->key_head));
-  ic_info->used -= ic_info->key_head;
-  ic_info->state_key_head = ic_info->key_head = 0;
+       if (translated == (char *) MTEXT_DATA (mt))
+         translated = dgettext ("m17n-contrib", (char *) MTEXT_DATA (mt));
+       if (translated != (char *) MTEXT_DATA (mt))
+         {
+           M17N_OBJECT_UNREF (mt);
+           mt = mtext__from_data (translated, strlen (translated),
+                                  MTEXT_FORMAT_UTF_8, 1);
+         }
+      }
+#endif
+      mplist_set (plist, Mtext, mt);
+      M17N_OBJECT_UNREF (mt);
+      return 1;
+    }
+  if (MFAILP (MPLIST_SYMBOL_P (plist) && MPLIST_SYMBOL (plist) == Mnil))
+    return 0;
+  return 1;
 }
 
+
+/* Check KEYSEQ, and return 1 if it is valid as a key sequence, return
+   0 if not.  */
+
 static int
-new_index (MInputContext *ic, int current, int limit, MSymbol sym, MText *mt)
+check_command_keyseq (MPlist *keyseq)
 {
-  int code = marker_code (sym);
+  if (MPLIST_PLIST_P (keyseq))
+    {
+      MPlist *p = MPLIST_PLIST (keyseq);
 
-  if (mt && (code == '[' || code == ']'))
+      MPLIST_DO (p, p)
+       if (! MPLIST_SYMBOL_P (p) && ! MPLIST_INTEGER_P (p))
+         return 0;
+      return 1;
+    }
+  if (MPLIST_MTEXT_P (keyseq))
     {
-      int pos = current;
+      MText *mt = MPLIST_MTEXT (keyseq);
+      int i;
+      
+      for (i = 0; i < mtext_nchars (mt); i++)
+       if (mtext_ref_char (mt, i) >= 256)
+         return 0;
+      return 1;
+    }
+  return 0;
+}
 
-      if (code == '[' && current > 0)
+/* Load command defitions from PLIST into IM_INFO->cmds.
+
+   PLIST is well-formed and has this form;
+     (command (NAME [DESCRIPTION KEYSEQ ...]) ...)
+   NAME is a symbol.  DESCRIPTION is an M-text or `nil'.  KEYSEQ is an
+   M-text or a plist of symbols.
+
+   The returned list has the same form, but for each element...
+
+   (1) If DESCRIPTION and the rest are omitted, the element is not
+   stored in the returned list.
+
+   (2) If DESCRIPTION is nil, it is complemented by the corresponding
+   description in global_info->cmds (if any).  */
+
+static void
+load_commands (MInputMethodInfo *im_info, MPlist *plist)
+{
+  MPlist *tail;
+
+  im_info->cmds = tail = mplist ();
+
+  MPLIST_DO (plist, MPLIST_NEXT (plist))
+    {
+      /* PLIST ::= ((NAME DESC KEYSEQ ...) ...) */
+      MPlist *pl, *p;
+
+      if (MFAILP (MPLIST_PLIST_P (plist)))
+       continue;
+      pl = MPLIST_PLIST (plist); /* PL ::= (NAME DESC KEYSEQ ...) */
+      if (MFAILP (MPLIST_SYMBOL_P (pl)))
+       continue;
+      p = MPLIST_NEXT (pl);    /* P ::= (DESC KEYSEQ ...) */
+      if (MPLIST_TAIL_P (p))   /* PL ::= (NAME) */
        {
-         if (mtext_prop_range (mt, Mcandidate_list, pos - 1, &pos, NULL, 1)
-             && pos > 0)
-           current = pos;
+         if (MFAILP (im_info != global_info))
+           mplist_add (p, Msymbol, Mnil); /* PL ::= (NAME nil) */
        }
-      else if (code == ']' && current < mtext_nchars (mt))
+      else
        {
-         if (mtext_prop_range (mt, Mcandidate_list, pos, NULL, &pos, 1))
-           current = pos;
+         if (! check_description (p))
+           mplist_set (p, Msymbol, Mnil);
+         p = MPLIST_NEXT (p);
+         while (! MPLIST_TAIL_P (p))
+           {
+             if (MFAILP (check_command_keyseq (p)))
+               mplist__pop_unref (p);
+             else
+               p = MPLIST_NEXT (p);
+           }
        }
-      return current;
+      tail = mplist_add (tail, Mplist, pl);
     }
-  if (code >= 0)
-    return (code == '<' ? 0
-           : code == '>' ? limit
-           : code == '-' ? current - 1
-           : code == '+' ? current + 1
-           : code == '=' ? current
-           : code - '0' > limit ? limit
-           : code - '0');
-  if (! ic)  
-    return 0;
-  return (int) mplist_get (((MInputContextInfo *) ic->info)->markers, sym);
 }
 
-static void
-update_candidate (MInputContext *ic, MTextProperty *prop, int idx)
+static MPlist *
+config_command (MPlist *plist, MPlist *global_cmds, MPlist *custom_cmds,
+               MPlist *config_cmds)
 {
-  int from = mtext_property_start (prop);
-  int to = mtext_property_end (prop);
-  int start;
-  MPlist *candidate_list = mtext_property_value (prop);
-  MPlist *group = find_candidates_group (candidate_list, idx, &start,
-                                        NULL, NULL);
-  int ingroup_index = idx - start;
-  MText *mt;
+  MPlist *global = NULL, *custom = NULL, *config = NULL;
+  MSymbol name = MPLIST_SYMBOL (plist);
+  MSymbol status;
+  MPlist *description, *keyseq;
 
-  preedit_delete (ic, from, to);
-  if (MPLIST_MTEXT_P (group))
+  if (global_cmds && (global = mplist__assq (global_cmds, name)))
+    global = MPLIST_NEXT (MPLIST_PLIST (global));  
+
+  plist = MPLIST_NEXT (plist);
+  if (MPLIST_MTEXT_P (plist) || MPLIST_PLIST_P (plist))
     {
-      mt = MPLIST_MTEXT (group);
-      preedit_insert (ic, from, NULL, mtext_ref_char (mt, ingroup_index));
-      to = from + 1;
+      description = plist;
+      plist = MPLIST_NEXT (plist);
     }
   else
     {
-      int i;
-      MPlist *plist;
+      description = global;
+      if (! MPLIST_TAIL_P (plist))
+       plist = MPLIST_NEXT (plist);
+    }
+  if (MPLIST_TAIL_P (plist) && global)
+    {
+      keyseq = MPLIST_NEXT (global);
+      status = Minherited;
+    }
+  else
+    {
+      keyseq = plist;
+      status = Mnil;
+    }
 
-      for (i = 0, plist = MPLIST_PLIST (group); i < ingroup_index;
-          i++, plist = MPLIST_NEXT (plist));
-      mt = MPLIST_MTEXT (plist);
-      preedit_insert (ic, from, mt, 0);
-      to = from + mtext_nchars (mt);
+  if (config_cmds && (config = mplist__assq (config_cmds, name)))
+    {
+      status = Mconfigured;
+      config = MPLIST_NEXT (MPLIST_PLIST (config));
+      if (! MPLIST_TAIL_P (config))
+       keyseq = config;
     }
-  mtext_put_prop (ic->preedit, from, to, Mcandidate_list, candidate_list);
-  mtext_put_prop (ic->preedit, from, to, Mcandidate_index, (void *) idx);
-  ic->cursor_pos = to;
+  else if (custom_cmds && (custom = mplist__assq (custom_cmds, name)))
+    {
+      MPlist *this_keyseq = MPLIST_NEXT (MPLIST_NEXT (MPLIST_PLIST (custom)));
+
+      if (MPLIST_TAIL_P (this_keyseq))
+       mplist__pop_unref (custom);
+      else
+       {
+         status = Mcustomized;
+         keyseq = this_keyseq;
+       }
+    }
+  
+  plist = mplist ();
+  mplist_add (plist, Msymbol, name);
+  if (description)
+    mplist_add (plist, MPLIST_KEY (description), MPLIST_VAL (description));
+  else
+    mplist_add (plist, Msymbol, Mnil);
+  mplist_add (plist, Msymbol, status);
+  mplist__conc (plist, keyseq);
+  return plist;
 }
 
-static MCharset *
-get_select_charset (MInputContextInfo * ic_info)
+static void
+config_all_commands (MInputMethodInfo *im_info)
 {
-  MPlist *plist = resolve_variable (ic_info, Mcandidates_charset);
-  MSymbol sym;
+  MPlist *global_cmds, *custom_cmds, *config_cmds;
+  MInputMethodInfo *temp;
+  MPlist *tail, *plist;
 
-  if (! MPLIST_VAL (plist))
-    return NULL;
-  sym = MPLIST_SYMBOL (plist);
-  if (sym == Mnil)
-    return NULL;
-  return MCHARSET (sym);
+  M17N_OBJECT_UNREF (im_info->configured_cmds);
+
+  if (MPLIST_TAIL_P (im_info->cmds)
+      || ! im_info->mdb)
+    return;
+
+  global_cmds = im_info != global_info ? global_info->cmds : NULL;
+  custom_cmds = ((temp = get_custom_info (im_info)) ? temp->cmds : NULL);
+  config_cmds = ((temp = get_config_info (im_info)) ? temp->cmds : NULL);
+
+  im_info->configured_cmds = tail = mplist ();
+  MPLIST_DO (plist, im_info->cmds)
+    {
+      MPlist *pl = config_command (MPLIST_PLIST (plist),
+                                  global_cmds, custom_cmds, config_cmds);
+      if (pl)
+       {
+         tail = mplist_add (tail, Mplist, pl);
+         M17N_OBJECT_UNREF (pl);
+       }
+    }
 }
 
-static MPlist *
-adjust_candidates (MPlist *plist, MCharset *charset)
+/* Check VAL's value against VALID_VALUES, and return 1 if it is
+   valid, return 0 if not.  */
+
+static int
+check_variable_value (MPlist *val, MPlist *global)
 {
-  MPlist *pl;
+  MSymbol type = MPLIST_KEY (val);
+  MPlist *valids = MPLIST_NEXT (val);
 
-  /* plist ::= MTEXT ... | PLIST ... */
-  plist = mplist_copy (plist);
-  if (MPLIST_MTEXT_P (plist))
+  if (type != Minteger && type != Mtext && type != Msymbol)
+    return 0;
+  if (global)
     {
-      pl = plist;
-      while (! MPLIST_TAIL_P (pl))
-       {
-         /* pl ::= MTEXT ... */
-         MText *mt = MPLIST_MTEXT (pl);
-         int mt_copied = 0;
-         int i, c;
+      if (MPLIST_KEY (global) != Mt
+         && MPLIST_KEY (global) != MPLIST_KEY (val))
+       return 0;
+      if (MPLIST_TAIL_P (valids))
+       valids = MPLIST_NEXT (global);
+    }
+  if (MPLIST_TAIL_P (valids))
+    return 1;
 
-         for (i = mtext_nchars (mt) - 1; i >= 0; i--)
+  if (type == Minteger)
+    {
+      int n = MPLIST_INTEGER (val);
+
+      MPLIST_DO (valids, valids)
+       {
+         if (MPLIST_INTEGER_P (valids))
            {
-             c = mtext_ref_char (mt, i);
-             if (ENCODE_CHAR (charset, c) == MCHAR_INVALID_CODE)
-               {
-                 if (! mt_copied)
-                   {
-                     mt = mtext_dup (mt);
-                     mplist_set (pl, Mtext, mt);
-                     M17N_OBJECT_UNREF (mt);
-                     mt_copied = 1;
-                   }
-                 mtext_del (mt, i, i + 1);
-               }
+             if (n == MPLIST_INTEGER (valids))
+               break;
            }
-         if (mtext_len (mt) > 0)
-           pl = MPLIST_NEXT (pl);
-         else
+         else if (MPLIST_PLIST_P (valids))
            {
-             mplist_pop (pl);
-             M17N_OBJECT_UNREF (mt);
+             MPlist *p = MPLIST_PLIST (valids);
+             int min_bound, max_bound;
+
+             if (! MPLIST_INTEGER_P (p))
+               MERROR (MERROR_IM, 0);
+             min_bound = MPLIST_INTEGER (p);
+             p = MPLIST_NEXT (p);
+             if (! MPLIST_INTEGER_P (p))
+               MERROR (MERROR_IM, 0);
+             max_bound = MPLIST_INTEGER (p);
+             if (n >= min_bound && n <= max_bound)
+               break;
            }
        }
     }
-  else                         /* MPLIST_PLIST_P (plist) */
+  else if (type == Msymbol)
     {
-      pl = plist;
-      while (! MPLIST_TAIL_P (pl))
+      MSymbol sym = MPLIST_SYMBOL (val);
+
+      MPLIST_DO (valids, valids)
        {
-         /* pl ::= (MTEXT ...) ... */
-         MPlist *p = MPLIST_PLIST (pl);
-         int p_copied = 0;
-         /* p ::= MTEXT ... */
-         MPlist *p0 = p;
-         int n = 0;
+         if (! MPLIST_SYMBOL_P (valids))
+           MERROR (MERROR_IM, 0);
+         if (sym == MPLIST_SYMBOL (valids))
+           break;
+       }
+    }
+  else
+    {
+      MText *mt = MPLIST_MTEXT (val);
 
-         while (! MPLIST_TAIL_P (p0))
+      MPLIST_DO (valids, valids)
+       {
+         if (! MPLIST_MTEXT_P (valids))
+           MERROR (MERROR_IM, 0);
+         if (mtext_cmp (mt, MPLIST_MTEXT (valids)) == 0)
+           break;
+       }
+    }
+
+  return (! MPLIST_TAIL_P (valids));
+}
+
+/* Load variable defitions from PLIST into IM_INFO->vars.
+
+   PLIST is well-formed and has this form;
+     ((NAME [DESCRIPTION DEFAULT-VALUE VALID-VALUE ...])
+      ...)
+   NAME is a symbol.  DESCRIPTION is an M-text or `nil'.
+
+   The returned list has the same form, but for each element...
+
+   (1) If DESCRIPTION and the rest are omitted, the element is not
+   stored in the returned list.
+
+   (2) If DESCRIPTION is nil, it is complemented by the corresponding
+   description in global_info->vars (if any).  */
+
+static void
+load_variables (MInputMethodInfo *im_info, MPlist *plist)
+{
+  MPlist *global_vars = ((im_info->mdb && im_info != global_info)
+                        ? global_info->vars : NULL);
+  MPlist *tail;
+
+  im_info->vars = tail = mplist ();
+  MPLIST_DO (plist, MPLIST_NEXT (plist))
+    {
+      MPlist *pl, *p;
+
+      if (MFAILP (MPLIST_PLIST_P (plist)))
+       continue;
+      pl = MPLIST_PLIST (plist); /* PL ::= (NAME DESC VALUE VALID ...) */
+      if (MFAILP (MPLIST_SYMBOL_P (pl)))
+       continue;
+      if (im_info == global_info)
+       {
+         /* Loading a global variable.  */
+         p = MPLIST_NEXT (pl);
+         if (MPLIST_TAIL_P (p))
+           mplist_add (p, Msymbol, Mnil);
+         else
            {
-             MText *mt = MPLIST_MTEXT (p0);
-             int i, c;
+             if (! check_description (p))
+               mplist_set (p, Msymbol, Mnil);
+             p = MPLIST_NEXT (p);
+             if (MFAILP (! MPLIST_TAIL_P (p)
+                         && check_variable_value (p, NULL)))
+               mplist_set (p, Mt, NULL);
+           }
+       }
+      else if (im_info->mdb)
+       {
+         /* Loading a local variable.  */
+         MSymbol name = MPLIST_SYMBOL (pl);
+         MPlist *global = NULL;
 
-             for (i = mtext_nchars (mt) - 1; i >= 0; i--)
-               {
-                 c = mtext_ref_char (mt, i);
-                 if (ENCODE_CHAR (charset, c) == MCHAR_INVALID_CODE)
-                   break;
-               }
-             if (i < 0)
-               {
-                 p0 = MPLIST_NEXT (p0);
-                 n++;
-               }
+         if (global_vars
+             && (p = mplist__assq (global_vars, name)))
+           {
+             /* P ::= ((NAME DESC ...) ...) */
+             p = MPLIST_PLIST (p); /* P ::= (NAME DESC ...) */
+             global = MPLIST_NEXT (p); /* P ::= (DESC VALUE ...) */
+             global = MPLIST_NEXT (global); /* P ::= (VALUE ...) */
+           }
+
+         p = MPLIST_NEXT (pl); /* P ::= (DESC VALUE VALID ...) */
+         if (! MPLIST_TAIL_P (p))
+           {
+             if (! check_description (p))
+               mplist_set (p, Msymbol, Mnil);
+             p = MPLIST_NEXT (p); /* P ::= (VALUE VALID ...) */
+             if (MFAILP (! MPLIST_TAIL_P (p)))
+               mplist_set (p, Mt, NULL);
              else
                {
-                 if (! p_copied)
-                   {
-                     p = mplist_copy (p);
-                     mplist_set (pl, Mplist, p);
-                     M17N_OBJECT_UNREF (p);
-                     p_copied = 1;
-                     p0 = p;
-                     while (n-- > 0)
-                       p0 = MPLIST_NEXT (p0);
-                   }     
-                 mplist_pop (p0);
-                 M17N_OBJECT_UNREF (mt);
+                 MPlist *valid_values = MPLIST_NEXT (p);
+
+                 if (! MPLIST_TAIL_P (valid_values)
+                     ? MFAILP (check_variable_value (p, NULL))
+                     : global && MFAILP (check_variable_value (p, global)))
+                   mplist_set (p, Mt, NULL);
                }
            }
-         if (! MPLIST_TAIL_P (p))
-           pl = MPLIST_NEXT (pl);
-         else
-           {
-             mplist_pop (pl);
-             M17N_OBJECT_UNREF (p);
-           }
        }
+      else
+       {
+         /* Loading a variable customization.  */
+         p = MPLIST_NEXT (pl); /* P ::= (nil VALUE) */
+         if (MFAILP (! MPLIST_TAIL_P (p)))
+           continue;
+         p = MPLIST_NEXT (p);  /* P ::= (VALUE) */
+         if (MFAILP (MPLIST_INTEGER_P (p) || MPLIST_SYMBOL_P (p)
+                     || MPLIST_MTEXT_P (p)))
+           continue;
+       }
+      tail = mplist_add (tail, Mplist, pl);
     }
-  if (MPLIST_TAIL_P (plist))
-    {
-      M17N_OBJECT_UNREF (plist);
-      return NULL;
-    }      
-  return plist;
 }
 
 static MPlist *
-get_candidate_list (MInputContextInfo *ic_info, MPlist *args)
+config_variable (MPlist *plist, MPlist *global_vars, MPlist *custom_vars,
+                MPlist *config_vars)
 {
-  MCharset *charset = get_select_charset (ic_info);
-  MPlist *plist;
-  int column;
-  int i, len;
-
-  plist = resolve_variable (ic_info, Mcandidates_group_size);
-  column = MPLIST_INTEGER (plist);
+  MPlist *global = NULL, *custom = NULL, *config = NULL;
+  MSymbol name = MPLIST_SYMBOL (plist);
+  MSymbol status;
+  MPlist *description = NULL, *value, *valids;
 
-  plist = MPLIST_PLIST (args);
-  if (charset)
+  if (global_vars)
     {
-      if (! (plist = adjust_candidates (plist, charset)))
-       return NULL;
+      global = mplist__assq (global_vars, name);
+      if (global)
+       global = MPLIST_NEXT (MPLIST_PLIST (global)); /* (DESC VALUE ...) */
     }
-  else
-    M17N_OBJECT_REF (plist);
 
-  if (column > 0)
+  plist = MPLIST_NEXT (plist);
+  if (MPLIST_MTEXT_P (plist) || MPLIST_PLIST_P (plist))
+    description = plist;
+  else if (global)
+    description = global;
+  if (global)
+    global = MPLIST_NEXT (global); /* (VALUE VALIDS ...) */
+
+  if (MPLIST_TAIL_P (plist))
     {
-      if (MPLIST_MTEXT_P (plist))
+      /* Inherit from global (if any).  */
+      if (global)
        {
-         MText *mt = MPLIST_MTEXT (plist);
-         MPlist *next = MPLIST_NEXT (plist);
-
-         if (MPLIST_TAIL_P (next))
-           M17N_OBJECT_REF (mt);
-         else
-           {
-             mt = mtext_dup (mt);
-             while (! MPLIST_TAIL_P (next))
-               {
-                 mt = mtext_cat (mt, MPLIST_MTEXT (next));
-                 next = MPLIST_NEXT (next);
-               }
-           }
-         M17N_OBJECT_UNREF (plist);
-         plist = mplist ();
-         len = mtext_nchars (mt);
-         if (len <= column)
-           mplist_add (plist, Mtext, mt);
-         else
-           {
-             for (i = 0; i < len; i += column)
-               {
-                 int to = (i + column < len ? i + column : len);
-                 MText *sub = mtext_copy (mtext (), 0, mt, i, to);
-                                                      
-                 mplist_add (plist, Mtext, sub);
-                 M17N_OBJECT_UNREF (sub);
-               }
-           }
-         M17N_OBJECT_UNREF (mt);
+         value = global;
+         if (MPLIST_KEY (value) == Mt)
+           value = NULL;
+         valids = MPLIST_NEXT (global);
+         status = Minherited;
        }
-      else             /* MPLIST_PLIST_P (plist) */
+      else
        {
-         MPlist *pl = MPLIST_PLIST (plist), *p;
-         MPlist *next = MPLIST_NEXT (plist);
-         int j;
-
-         if (MPLIST_TAIL_P (next))
-           M17N_OBJECT_REF (pl);
-         else
-           {
-             pl = mplist_copy (pl);
-             while (! MPLIST_TAIL_P (next))
-               {
-                 p = mplist_copy (MPLIST_PLIST (next));
-                 pl = mplist__conc (pl, p);
-                 M17N_OBJECT_UNREF (p);
-                 next = MPLIST_NEXT (next);
-               }
-           }
-         M17N_OBJECT_UNREF (plist);
-         plist = mplist ();
-         len = mplist_length (pl);
-         if (len <= column)
-           mplist_add (plist, Mplist, pl);
-         else
-           {
-             MPlist *p0 = pl;
+         value = NULL;
+         valids = NULL;
+         status = Mnil;
+         plist = NULL;
+       }
+    }
+  else
+    {
+      value = plist = MPLIST_NEXT (plist);
+      valids = MPLIST_NEXT (value);
+      if (MPLIST_KEY (value) == Mt)
+       value = NULL;
+      if (! MPLIST_TAIL_P (valids))
+       global = NULL;
+      else if (global)
+       valids = MPLIST_NEXT (global);
+      status = Mnil;
+    }
 
-             for (i = 0; i < len; i += column)
-               {
-                 p = mplist ();
-                 mplist_add (plist, Mplist, p);
-                 M17N_OBJECT_UNREF (p);
-                 for (j = 0; j < column && i + j < len; j++)
-                   {
-                     p = mplist_add (p, Mtext, MPLIST_VAL (p0));
-                     p0 = MPLIST_NEXT (p0);
-                   }
-               }
-           }
-         M17N_OBJECT_UNREF (pl);
+  if (config_vars && (config = mplist__assq (config_vars, name)))
+    {
+      status = Mconfigured;
+      config = MPLIST_NEXT (MPLIST_PLIST (config));
+      if (! MPLIST_TAIL_P (config))
+       {
+         value = config;
+         if (MFAILP (check_variable_value (value, global ? global : plist)))
+           value = NULL;
        }
     }
+  else if (custom_vars && (custom = mplist__assq (custom_vars, name)))
+    {
+      MPlist *this_value = MPLIST_NEXT (MPLIST_NEXT (MPLIST_PLIST (custom)));
 
+      if (MPLIST_TAIL_P (this_value))
+       mplist__pop_unref (custom);
+      else
+       {
+         value = this_value;
+         if (MFAILP (check_variable_value (value, global ? global : plist)))
+           value = NULL;
+         status = Mcustomized;
+       }
+    }
+  
+  plist = mplist ();
+  mplist_add (plist, Msymbol, name);
+  if (description)
+    mplist_add (plist, MPLIST_KEY (description), MPLIST_VAL (description));
+  else
+    mplist_add (plist, Msymbol, Mnil);
+  mplist_add (plist, Msymbol, status);
+  if (value)
+    mplist_add (plist, MPLIST_KEY (value), MPLIST_VAL (value));
+  else
+    mplist_add (plist, Mt, NULL);
+  if (valids && ! MPLIST_TAIL_P (valids))
+    mplist__conc (plist, valids);
   return plist;
 }
 
+/* Return a configured variable definition list based on
+   IM_INFO->vars.  If a variable in it doesn't contain a value, try to
+   get it from global_info->vars.  */
 
-static MPlist *
-regularize_action (MPlist *action_list)
+static void
+config_all_variables (MInputMethodInfo *im_info)
 {
-  MPlist *action = NULL;
-  MSymbol name;
-  MPlist *args;
+  MPlist *global_vars, *custom_vars, *config_vars;
+  MInputMethodInfo *temp;
+  MPlist *tail, *plist;
 
-  if (MPLIST_PLIST_P (action_list))
+  M17N_OBJECT_UNREF (im_info->configured_vars);
+
+  if (MPLIST_TAIL_P (im_info->vars)
+      || ! im_info->mdb)
+    return;
+
+  global_vars = im_info != global_info ? global_info->vars : NULL;
+  custom_vars = ((temp = get_custom_info (im_info)) ? temp->vars : NULL);
+  config_vars = ((temp = get_config_info (im_info)) ? temp->vars : NULL);
+
+  im_info->configured_vars = tail = mplist ();
+  MPLIST_DO (plist, im_info->vars)
     {
-      action = MPLIST_PLIST (action_list);
-      if (MPLIST_SYMBOL_P (action))
-       {
-         name = MPLIST_SYMBOL (action);
-         args = MPLIST_NEXT (action);
-         if (name == Minsert
-             && MPLIST_PLIST_P (args))
-           mplist_set (action, Msymbol, M_candidates);
-       }
-      else if (MPLIST_MTEXT_P (action) || MPLIST_PLIST_P (action))
+      MPlist *pl = config_variable (MPLIST_PLIST (plist),
+                                   global_vars, custom_vars, config_vars);
+      if (pl)
        {
-         action = mplist ();
-         mplist_push (action, Mplist, MPLIST_VAL (action_list));
-         mplist_push (action, Msymbol, M_candidates);
-         mplist_set (action_list, Mplist, action);
-         M17N_OBJECT_UNREF (action);
+         tail = mplist_add (tail, Mplist, pl);
+         M17N_OBJECT_UNREF (pl);
        }
     }
-  else if (MPLIST_MTEXT_P (action_list) || MPLIST_INTEGER_P (action_list))
+}
+
+/* Load an input method (LANGUAGE NAME) from PLIST into IM_INFO.
+   CONFIG contains configuration information of the input method.  */
+
+static void
+load_im_info (MPlist *plist, MInputMethodInfo *im_info)
+{
+  MPlist *pl, *p;
+
+  if (! im_info->cmds && (pl = mplist__assq (plist, Mcommand)))
     {
-      action = mplist ();
-      mplist_push (action, MPLIST_KEY (action_list), MPLIST_VAL (action_list));
-      mplist_push (action, Msymbol, Minsert);
-      mplist_set (action_list, Mplist, action);
-      M17N_OBJECT_UNREF (action);
+      load_commands (im_info, MPLIST_PLIST (pl));
+      config_all_commands (im_info);
+      pl = mplist_pop (pl);
+      M17N_OBJECT_UNREF (pl);
     }
-  return action;
+
+  if (! im_info->vars && (pl = mplist__assq (plist, Mvariable)))
+    {
+      load_variables (im_info, MPLIST_PLIST (pl));
+      config_all_variables (im_info);
+      pl = mplist_pop (pl);
+      M17N_OBJECT_UNREF (pl);
+    }
+
+  MPLIST_DO (plist, plist)
+    if (MPLIST_PLIST_P (plist))
+      {
+       MPlist *elt = MPLIST_PLIST (plist);
+       MSymbol key;
+
+       if (MFAILP (MPLIST_SYMBOL_P (elt)))
+         continue;
+       key = MPLIST_SYMBOL (elt);
+       if (key == Mtitle)
+         {
+           if (im_info->title)
+             continue;
+           elt = MPLIST_NEXT (elt);
+           if (MFAILP (MPLIST_MTEXT_P (elt)))
+             continue;
+           im_info->title = MPLIST_MTEXT (elt);
+           M17N_OBJECT_REF (im_info->title);
+         }
+       else if (key == Mmap)
+         {
+           pl = mplist__from_alist (MPLIST_NEXT (elt));
+           if (MFAILP (pl))
+             continue;
+           if (! im_info->maps)
+             im_info->maps = pl;
+           else
+             {
+               mplist__conc (im_info->maps, pl);
+               M17N_OBJECT_UNREF (pl);
+             }
+         }
+       else if (key == Mmacro)
+         {
+           if (! im_info->macros)
+             im_info->macros = mplist ();
+           MPLIST_DO (elt, MPLIST_NEXT (elt))
+             {
+               if (MFAILP (MPLIST_PLIST_P (elt)))
+                 continue;
+               load_macros (im_info, MPLIST_PLIST (elt));
+             }
+         }
+       else if (key == Mmodule)
+         {
+           if (! im_info->externals)
+             im_info->externals = mplist ();
+           MPLIST_DO (elt, MPLIST_NEXT (elt))
+             {
+               MIMExternalModule *external;
+
+               if (MFAILP (MPLIST_PLIST_P (elt)))
+                 continue;
+               external = load_external_module (MPLIST_PLIST (elt));
+               if (external)
+                 mplist_add (im_info->externals, external->name, external);
+             }
+         }
+       else if (key == Mstate)
+         {
+           MPLIST_DO (elt, MPLIST_NEXT (elt))
+             {
+               MIMState *state;
+
+               if (MFAILP (MPLIST_PLIST_P (elt)))
+                 continue;
+               pl = MPLIST_PLIST (elt);
+               if (! im_info->states)
+                 im_info->states = mplist ();
+               state = load_state (im_info, MPLIST_PLIST (elt));
+               if (MFAILP (state))
+                 continue;
+               mplist_put (im_info->states, state->name, state);
+             }
+         }
+       else if (key == Minclude)
+         {
+           /* elt ::= include (tag1 tag2 ...) key item ... */
+           MSymbol key;
+           MInputMethodInfo *temp;
+
+           elt = MPLIST_NEXT (elt);
+           if (MFAILP (MPLIST_PLIST_P (elt)))
+             continue;
+           temp = get_im_info_by_tags (MPLIST_PLIST (elt));
+           if (MFAILP (temp))
+             continue;
+           elt = MPLIST_NEXT (elt);
+           if (MFAILP (MPLIST_SYMBOL_P (elt)))
+             continue;
+           key = MPLIST_SYMBOL (elt);
+           elt = MPLIST_NEXT (elt);
+           if (key == Mmap)
+             {
+               if (! temp->maps || MPLIST_TAIL_P (temp->maps))
+                 continue;
+               if (! im_info->maps)
+                 im_info->maps = mplist ();
+               MPLIST_DO (pl, temp->maps)
+                 {
+                   p = MPLIST_VAL (pl);
+                   MPLIST_ADD_PLIST (im_info->maps, MPLIST_KEY (pl), p);
+                   M17N_OBJECT_REF (p);
+                 }
+             }
+           else if (key == Mmacro)
+             {
+               if (! temp->macros || MPLIST_TAIL_P (temp->macros))
+                 continue;
+               if (! im_info->macros)
+                 im_info->macros = mplist ();
+               MPLIST_DO (pl, temp->macros)
+                 {
+                   p = MPLIST_VAL (pl);
+                   MPLIST_ADD_PLIST (im_info->macros, MPLIST_KEY (pl), p);
+                   M17N_OBJECT_REF (p);
+                 }
+             }
+           else if (key == Mstate)
+             {
+               if (! temp->states || MPLIST_TAIL_P (temp->states))
+                 continue;
+               if (! im_info->states)
+                 im_info->states = mplist ();
+               MPLIST_DO (pl, temp->states)
+                 {
+                   MIMState *state = MPLIST_VAL (pl);
+
+                   mplist_add (im_info->states, MPLIST_KEY (pl), state);
+                   M17N_OBJECT_REF (state);
+                 }
+             }
+         }
+       else if (key == Mdescription)
+         {
+           if (im_info->description)
+             continue;
+           elt = MPLIST_NEXT (elt);
+           if (! check_description (elt))
+             continue;
+           im_info->description = MPLIST_MTEXT (elt);
+           M17N_OBJECT_REF (im_info->description);
+         }
+      }
+  if (im_info->macros)
+    {
+      MPLIST_DO (pl, im_info->macros)
+       parse_action_list (MPLIST_PLIST (pl), im_info->macros);
+    }
+
+  im_info->tick = time (NULL);
 }
 
-static int
-take_action_list (MInputContext *ic, MPlist *action_list)
+\f
+
+static int take_action_list (MInputContext *ic, MPlist *action_list);
+static void preedit_commit (MInputContext *ic, int need_prefix);
+
+/* Shift to the state of name STATE_NAME.  If STATE_NAME is `t', shift
+   to the previous state (if any).  If STATE_NAME is `nil', shift to
+   the initial state.  */
+
+static void
+shift_state (MInputContext *ic, MSymbol state_name)
 {
+  MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
   MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
-  MPlist *candidate_list = ic->candidate_list;
-  int candidate_index = ic->candidate_index;
-  int candidate_show = ic->candidate_show;
-  MTextProperty *prop;
+  MIMState *orig_state = ic_info->state, *state;
 
-  MPLIST_DO (action_list, action_list)
+  /* Find a state to shift to.  If not found, shift to the initial
+     state.  */
+  if (state_name == Mt)
     {
-      MPlist *action = regularize_action (action_list);
-      MSymbol name;
-      MPlist *args;
+      if (! ic_info->prev_state)
+       return;
+      state = ic_info->prev_state;
+    }
+  else if (state_name == Mnil)
+    {
+      state = (MIMState *) MPLIST_VAL (im_info->states);
+    }
+  else
+    {
+      state = (MIMState *) mplist_get (im_info->states, state_name);
+      if (! state)
+       state = (MIMState *) MPLIST_VAL (im_info->states);
+    }
 
-      if (! action)
-       continue;
-      name = MPLIST_SYMBOL (action);
-      args = MPLIST_NEXT (action);
+  if (MDEBUG_FLAG ())
+    {
+      if (orig_state)
+       MDEBUG_PRINT2 ("\n  [IM] [%s] (shift %s)\n",
+                      MSYMBOL_NAME (orig_state->name),
+                      MSYMBOL_NAME (state->name));
+      else
+       MDEBUG_PRINT1 (" (shift %s)\n", MSYMBOL_NAME (state->name));
+    }
 
-      MDEBUG_PRINT1 (" %s", MSYMBOL_NAME (name));
-      if (name == Minsert)
+  /* Enter the new state.  */
+  ic_info->state = state;
+  ic_info->map = state->map;
+  ic_info->state_key_head = ic_info->key_head;
+  if (state == (MIMState *) MPLIST_VAL (im_info->states)
+      && orig_state)
+    /* We have shifted to the initial state.  */
+    preedit_commit (ic, 0);
+  mtext_cpy (ic_info->preedit_saved, ic->preedit);
+  ic_info->state_pos = ic->cursor_pos;
+  if (state != orig_state || state_name == Mnil)
+    {
+      if (state == (MIMState *) MPLIST_VAL (im_info->states))
        {
-         if (MPLIST_SYMBOL_P (args))
-           {
-             args = resolve_variable (ic_info, MPLIST_SYMBOL (args));
-             if (! MPLIST_MTEXT_P (args) && ! MPLIST_INTEGER_P (args))
-               continue;
-           }
-         if (MPLIST_MTEXT_P (args))
-           preedit_insert (ic, ic->cursor_pos, MPLIST_MTEXT (args), 0);
-         else                  /* MPLIST_INTEGER_P (args)) */
-           preedit_insert (ic, ic->cursor_pos, NULL, MPLIST_INTEGER (args));
+         /* Shifted to the initial state.  */
+         ic_info->prev_state = NULL;
+         M17N_OBJECT_UNREF (ic_info->vars_saved);
+         ic_info->vars_saved = mplist_copy (ic_info->vars);
        }
-      else if (name == M_candidates)
-       {
-         MPlist *plist = get_candidate_list (ic_info, args);
-         int len;
-
-         if (! plist)
-           continue;
-         if (MPLIST_MTEXT_P (plist))
-           {
-             preedit_insert (ic, ic->cursor_pos, NULL,
-                             mtext_ref_char (MPLIST_MTEXT (plist), 0));
-             len = 1;
-           }
-         else
-           {
-             MText * mt = MPLIST_MTEXT (MPLIST_PLIST (plist));
+      else
+       ic_info->prev_state = orig_state;
 
-             preedit_insert (ic, ic->cursor_pos, mt, 0);
-             len = mtext_nchars (mt);
-           }
-         mtext_put_prop (ic->preedit,
-                         ic->cursor_pos - len, ic->cursor_pos,
-                         Mcandidate_list, plist);
-         mtext_put_prop (ic->preedit,
-                         ic->cursor_pos - len, ic->cursor_pos,
-                         Mcandidate_index, (void *) 0);
-         M17N_OBJECT_UNREF (plist);
-       }
-      else if (name == Mselect)
-       {
-         int start, end;
-         int code, idx, gindex;
-         int pos = ic->cursor_pos;
-         MPlist *group;
+      if (state->title)
+       ic->status = state->title;
+      else
+       ic->status = im_info->title;
+      ic->status_changed = 1;
+      ic_info->state_hook = ic_info->map->map_actions;
+    }
+}
 
-         if (pos == 0
-             || ! (prop = mtext_get_property (ic->preedit, pos - 1,
-                                              Mcandidate_list)))
-           continue;
-         if (MPLIST_SYMBOL_P (args))
-           {
-             code = marker_code (MPLIST_SYMBOL (args));
-             if (code < 0)
-               continue;
-           }
-         else
-           code = -1;
-         idx = (int) mtext_get_prop (ic->preedit, pos - 1, Mcandidate_index);
-         group = find_candidates_group (mtext_property_value (prop), idx,
-                                        &start, &end, &gindex);
+/* Find a candidate group that contains a candidate number INDEX from
+   PLIST.  Set START_INDEX to the first candidate number of the group,
+   END_INDEX to the last candidate number plus 1, GROUP_INDEX to the
+   candidate group number if they are non-NULL.  If INDEX is -1, find
+   the last candidate group.  */
 
-         if (code != '[' && code != ']')
-           {
-             idx = (start
-                    + (code >= 0
-                       ? new_index (NULL, ic->candidate_index - start,
-                                    end - start - 1, MPLIST_SYMBOL (args),
-                                    NULL)
-                       : MPLIST_INTEGER (args)));
-             if (idx < 0)
-               {
-                 find_candidates_group (mtext_property_value (prop), -1,
-                                        NULL, &end, NULL);
-                 idx = end - 1;
-               }
-             else if (idx >= end
-                      && MPLIST_TAIL_P (MPLIST_NEXT (group)))
-               idx = 0;
-           }
-         else
-           {
-             int ingroup_index = idx - start;
-             int len;
+static MPlist *
+find_candidates_group (MPlist *plist, int index,
+                      int *start_index, int *end_index, int *group_index)
+{
+  int i = 0, gidx = 0, len;
 
-             group = mtext_property_value (prop);
-             len = mplist_length (group);
-             if (code == '[')
-               {
-                 gindex--;
-                 if (gindex < 0)
-                   gindex = len - 1;;
-               }
-             else
-               {
-                 gindex++;
-                 if (gindex >= len)
-                   gindex = 0;
-               }
-             for (idx = 0; gindex > 0; gindex--, group = MPLIST_NEXT (group))
-               idx += (MPLIST_MTEXT_P (group)
-                       ? mtext_nchars (MPLIST_MTEXT (group))
-                       : mplist_length (MPLIST_PLIST (group)));
-             len = (MPLIST_MTEXT_P (group)
-                    ? mtext_nchars (MPLIST_MTEXT (group))
-                    : mplist_length (MPLIST_PLIST (group)));
-             if (ingroup_index >= len)
-               ingroup_index = len - 1;
-             idx += ingroup_index;
-           }
-         update_candidate (ic, prop, idx);
+  MPLIST_DO (plist, plist)
+    {
+      if (MPLIST_MTEXT_P (plist))
+       len = mtext_nchars (MPLIST_MTEXT (plist));
+      else
+       len = mplist_length (MPLIST_PLIST (plist));
+      if (index < 0 ? MPLIST_TAIL_P (MPLIST_NEXT (plist))
+         : i + len > index)
+       {
+         if (start_index)
+           *start_index = i;
+         if (end_index)
+           *end_index = i + len;
+         if (group_index)
+           *group_index = gidx;
+         return plist;
        }
-      else if (name == Mshow)
-       ic->candidate_show = 1;
-      else if (name == Mhide)
-       ic->candidate_show = 0;
-      else if (name == Mdelete)
+      i += len;
+      gidx++;
+    }
+  return NULL;
+}
+
+/* Adjust markers for the change of preedit text.
+   If FROM == TO, the change is insertion of INS chars.
+   If FROM < TO and INS == 0, the change is deletion of the range.
+   If FROM < TO and INS > 0, the change is replacement.  */
+
+static void
+adjust_markers (MInputContext *ic, int from, int to, int ins)
+{
+  MInputContextInfo *ic_info = ((MInputContext *) ic)->info;
+  MPlist *markers;
+
+  if (from == to)
+    {
+      MPLIST_DO (markers, ic_info->markers)
+       if (MPLIST_INTEGER (markers) > from)
+         MPLIST_VAL (markers) = (void *) (MPLIST_INTEGER (markers) + ins);
+      if (ic->cursor_pos >= from)
+       ic->cursor_pos += ins;
+    }
+  else
+    {
+      MPLIST_DO (markers, ic_info->markers)
        {
-         int len = mtext_nchars (ic->preedit);
-         int pos;
-         int to;
+         if (MPLIST_INTEGER (markers) >= to)
+           MPLIST_VAL (markers)
+             = (void *) (MPLIST_INTEGER (markers) + ins - (to - from));
+         else if (MPLIST_INTEGER (markers) > from)
+           MPLIST_VAL (markers) = (void *) from;
+       }
+      if (ic->cursor_pos >= to)
+       ic->cursor_pos += ins - (to - from);
+      else if (ic->cursor_pos > from)
+       ic->cursor_pos = from;
+    }
+}
+
+
+static void
+preedit_insert (MInputContext *ic, int pos, MText *mt, int c)
+{
+  int nchars = mt ? mtext_nchars (mt) : 1;
+
+  if (mt)
+    {
+      mtext_ins (ic->preedit, pos, mt);
+      MDEBUG_PRINT1 ("(\"%s\")", MTEXT_DATA (mt));
+    }
+  else
+    {
+      mtext_ins_char (ic->preedit, pos, c, 1);
+      if (c < 0x7F)
+       MDEBUG_PRINT1 ("('%c')", c);
+      else
+       MDEBUG_PRINT1 ("(U+%04X)", c);
+    }
+  adjust_markers (ic, pos, pos, nchars);
+  ic->preedit_changed = 1;
+}
+
+
+static void
+preedit_delete (MInputContext *ic, int from, int to)
+{
+  mtext_del (ic->preedit, from, to);
+  adjust_markers (ic, from, to, 0);
+  ic->preedit_changed = 1;
+}
+
+static void
+preedit_replace (MInputContext *ic, int from, int to, MText *mt, int c)
+{
+  int ins;
+
+  mtext_del (ic->preedit, from, to);
+  if (mt)
+    {
+      mtext_ins (ic->preedit, from, mt);
+      ins = mtext_nchars (mt);
+    }
+  else
+    {
+      mtext_ins_char (ic->preedit, from, c, 1);
+      ins = 1;
+    }
+  adjust_markers (ic, from, to, ins);
+  ic->preedit_changed = 1;
+}
+
+
+static void
+preedit_commit (MInputContext *ic, int need_prefix)
+{
+  MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+  int preedit_len = mtext_nchars (ic->preedit);
+
+  if (preedit_len > 0)
+    {
+      MPlist *p;
+
+      mtext_put_prop_values (ic->preedit, 0, mtext_nchars (ic->preedit),
+                            Mcandidate_list, NULL, 0);
+      mtext_put_prop_values (ic->preedit, 0, mtext_nchars (ic->preedit),
+                            Mcandidate_index, NULL, 0);
+      mtext_cat (ic->produced, ic->preedit);
+      if (MDEBUG_FLAG ())
+       {
+         int i;
+
+         if (need_prefix)
+           MDEBUG_PRINT1 ("\n  [IM] [%s]",
+                          MSYMBOL_NAME (ic_info->state->name));
+         MDEBUG_PRINT (" (commit");
+         for (i = 0; i < mtext_nchars (ic->preedit); i++)
+           MDEBUG_PRINT1 (" U+%04X", mtext_ref_char (ic->preedit, i));
+         MDEBUG_PRINT (")");
+       }
+
+      mtext_reset (ic->preedit);
+      mtext_reset (ic_info->preedit_saved);
+      MPLIST_DO (p, ic_info->markers)
+       MPLIST_VAL (p) = 0;
+      ic->cursor_pos = ic_info->state_pos = 0;
+      ic->preedit_changed = 1;
+      ic_info->commit_key_head = ic_info->key_head;
+    }
+  if (ic->candidate_list)
+    {
+      M17N_OBJECT_UNREF (ic->candidate_list);
+      ic->candidate_list = NULL;
+      ic->candidate_index = 0;
+      ic->candidate_from = ic->candidate_to = 0;
+      ic->candidates_changed = MINPUT_CANDIDATES_LIST_CHANGED;
+      if (ic->candidate_show)
+       {
+         ic->candidate_show = 0;
+         ic->candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;
+       }
+    }
+}
+
+static int
+new_index (MInputContext *ic, int current, int limit, MSymbol sym, MText *mt)
+{
+  int code = marker_code (sym, 0);
+
+  if (mt && (code == '[' || code == ']'))
+    {
+      int pos = current;
+
+      if (code == '[' && current > 0)
+       {
+         if (mtext_prop_range (mt, Mcandidate_list, pos - 1, &pos, NULL, 1)
+             && pos > 0)
+           current = pos;
+       }
+      else if (code == ']' && current < mtext_nchars (mt))
+       {
+         if (mtext_prop_range (mt, Mcandidate_list, pos, NULL, &pos, 1))
+           current = pos;
+       }
+      return current;
+    }
+  if (code >= 0)
+    return (code == '<' ? 0
+           : code == '>' ? limit
+           : code == '-' ? current - 1
+           : code == '+' ? current + 1
+           : code == '=' ? current
+           : code - '0' > limit ? limit
+           : code - '0');
+  if (! ic)  
+    return 0;
+  return (int) mplist_get (((MInputContextInfo *) ic->info)->markers, sym);
+}
+
+static void
+update_candidate (MInputContext *ic, MTextProperty *prop, int idx)
+{
+  int from = mtext_property_start (prop);
+  int to = mtext_property_end (prop);
+  int start;
+  MPlist *candidate_list = mtext_property_value (prop);
+  MPlist *group = find_candidates_group (candidate_list, idx, &start,
+                                        NULL, NULL);
+  int ingroup_index = idx - start;
+  MText *mt;
+
+  candidate_list = mplist_copy (candidate_list);
+  if (MPLIST_MTEXT_P (group))
+    {
+      mt = MPLIST_MTEXT (group);
+      preedit_replace (ic, from, to, NULL, mtext_ref_char (mt, ingroup_index));
+      to = from + 1;
+    }
+  else
+    {
+      int i;
+      MPlist *plist;
+
+      for (i = 0, plist = MPLIST_PLIST (group); i < ingroup_index;
+          i++, plist = MPLIST_NEXT (plist));
+      mt = MPLIST_MTEXT (plist);
+      preedit_replace (ic, from, to, mt, 0);
+      to = from + mtext_nchars (mt);
+    }
+  mtext_put_prop (ic->preedit, from, to, Mcandidate_list, candidate_list);
+  M17N_OBJECT_UNREF (candidate_list);
+  mtext_put_prop (ic->preedit, from, to, Mcandidate_index, (void *) idx);
+  ic->cursor_pos = to;
+}
+
+static MCharset *
+get_select_charset (MInputContextInfo * ic_info)
+{
+  MPlist *plist = resolve_variable (ic_info, Mcandidates_charset);
+  MSymbol sym;
+
+  if (! MPLIST_VAL (plist))
+    return NULL;
+  sym = MPLIST_SYMBOL (plist);
+  if (sym == Mnil)
+    return NULL;
+  return MCHARSET (sym);
+}
+
+/* The returned plist must be UNREFed.  */
+
+static MPlist *
+adjust_candidates (MPlist *plist, MCharset *charset)
+{
+  MPlist *pl;
+
+  /* plist ::= MTEXT ... | PLIST ... */
+  plist = mplist_copy (plist);
+  if (MPLIST_MTEXT_P (plist))
+    {
+      pl = plist;
+      while (! MPLIST_TAIL_P (pl))
+       {
+         /* pl ::= MTEXT ... */
+         MText *mt = MPLIST_MTEXT (pl);
+         int mt_copied = 0;
+         int i, c;
+
+         for (i = mtext_nchars (mt) - 1; i >= 0; i--)
+           {
+             c = mtext_ref_char (mt, i);
+             if (ENCODE_CHAR (charset, c) == MCHAR_INVALID_CODE)
+               {
+                 if (! mt_copied)
+                   {
+                     mt = mtext_dup (mt);
+                     mplist_set (pl, Mtext, mt);
+                     M17N_OBJECT_UNREF (mt);
+                     mt_copied = 1;
+                   }
+                 mtext_del (mt, i, i + 1);
+               }
+           }
+         if (mtext_len (mt) > 0)
+           pl = MPLIST_NEXT (pl);
+         else
+           {
+             mplist_pop (pl);
+             M17N_OBJECT_UNREF (mt);
+           }
+       }
+    }
+  else                         /* MPLIST_PLIST_P (plist) */
+    {
+      pl = plist;
+      while (! MPLIST_TAIL_P (pl))
+       {
+         /* pl ::= (MTEXT ...) ... */
+         MPlist *p = MPLIST_PLIST (pl);
+         int p_copied = 0;
+         /* p ::= MTEXT ... */
+         MPlist *p0 = p;
+         int n = 0;
+
+         while (! MPLIST_TAIL_P (p0))
+           {
+             MText *mt = MPLIST_MTEXT (p0);
+             int i, c;
+
+             for (i = mtext_nchars (mt) - 1; i >= 0; i--)
+               {
+                 c = mtext_ref_char (mt, i);
+                 if (ENCODE_CHAR (charset, c) == MCHAR_INVALID_CODE)
+                   break;
+               }
+             if (i < 0)
+               {
+                 p0 = MPLIST_NEXT (p0);
+                 n++;
+               }
+             else
+               {
+                 if (! p_copied)
+                   {
+                     p = mplist_copy (p);
+                     mplist_set (pl, Mplist, p);
+                     M17N_OBJECT_UNREF (p);
+                     p_copied = 1;
+                     p0 = p;
+                     while (n-- > 0)
+                       p0 = MPLIST_NEXT (p0);
+                   }     
+                 mplist_pop (p0);
+                 M17N_OBJECT_UNREF (mt);
+               }
+           }
+         if (! MPLIST_TAIL_P (p))
+           pl = MPLIST_NEXT (pl);
+         else
+           {
+             mplist_pop (pl);
+             M17N_OBJECT_UNREF (p);
+           }
+       }
+    }
+  if (MPLIST_TAIL_P (plist))
+    {
+      M17N_OBJECT_UNREF (plist);
+      return NULL;
+    }      
+  return plist;
+}
+
+/* The returned Plist must be UNREFed.  */
+
+static MPlist *
+get_candidate_list (MInputContextInfo *ic_info, MPlist *args)
+{
+  MCharset *charset = get_select_charset (ic_info);
+  MPlist *plist;
+  int column;
+  int i, len;
+
+  plist = resolve_variable (ic_info, Mcandidates_group_size);
+  column = MPLIST_INTEGER (plist);
+
+  plist = MPLIST_PLIST (args);
+  if (! plist)
+    return NULL;
+  if (charset)
+    {
+      plist = adjust_candidates (plist, charset);
+      if (! plist)
+       return NULL;
+    }
+  else
+    M17N_OBJECT_REF (plist);
+
+  if (column == 0)
+    return plist;
+
+  if (MPLIST_MTEXT_P (plist))
+    {
+      MText *mt = MPLIST_MTEXT (plist);
+      MPlist *next = MPLIST_NEXT (plist);
+
+      if (MPLIST_TAIL_P (next))
+       M17N_OBJECT_REF (mt);
+      else
+       {
+         mt = mtext_dup (mt);
+         while (! MPLIST_TAIL_P (next))
+           {
+             mt = mtext_cat (mt, MPLIST_MTEXT (next));
+             next = MPLIST_NEXT (next);
+           }
+       }
+      M17N_OBJECT_UNREF (plist);
+      plist = mplist ();
+      len = mtext_nchars (mt);
+      if (len <= column)
+       mplist_add (plist, Mtext, mt);
+      else
+       {
+         for (i = 0; i < len; i += column)
+           {
+             int to = (i + column < len ? i + column : len);
+             MText *sub = mtext_copy (mtext (), 0, mt, i, to);
+                                                      
+             mplist_add (plist, Mtext, sub);
+             M17N_OBJECT_UNREF (sub);
+           }
+       }
+      M17N_OBJECT_UNREF (mt);
+    }
+  else if (MPLIST_PLIST_P (plist))
+    {
+      MPlist *tail = plist;
+      MPlist *new = mplist ();
+      MPlist *this = mplist ();
+      int count = 0;
+
+      MPLIST_DO (tail, tail)
+       {
+         MPlist *p = MPLIST_PLIST (tail);
+
+         MPLIST_DO (p, p)
+           {
+             MText *mt = MPLIST_MTEXT (p);
+
+             if (count == column)
+               {
+                 mplist_add (new, Mplist, this);
+                 M17N_OBJECT_UNREF (this);
+                 this = mplist ();
+                 count = 0;
+               }
+             mplist_add (this, Mtext, mt);
+             count++;
+           }
+       }
+      mplist_add (new, Mplist, this);
+      M17N_OBJECT_UNREF (this);
+      mplist_set (plist, Mnil, NULL);
+      MPLIST_DO (tail, new)
+       {
+         MPlist *elt = MPLIST_PLIST (tail);
+
+         mplist_add (plist, Mplist, elt);
+       }
+      M17N_OBJECT_UNREF (new);
+    }
+
+  return plist;
+}
+
+
+static MPlist *
+regularize_action (MPlist *action_list, MInputContextInfo *ic_info)
+{
+  MPlist *action = NULL;
+  MSymbol name;
+  MPlist *args;
+
+  if (MPLIST_SYMBOL_P (action_list))
+    {
+      MSymbol var = MPLIST_SYMBOL (action_list);
+      MPlist *p;
+
+      MPLIST_DO (p, ic_info->vars)
+       if (MPLIST_SYMBOL (MPLIST_PLIST (p)) == var)
+         break;
+      if (MPLIST_TAIL_P (p))
+       return NULL;
+      action = MPLIST_NEXT (MPLIST_PLIST (p));
+      mplist_set (action_list, MPLIST_KEY (action), MPLIST_VAL (action));
+    }
+
+  if (MPLIST_PLIST_P (action_list))
+    {
+      action = MPLIST_PLIST (action_list);
+      if (MPLIST_SYMBOL_P (action))
+       {
+         name = MPLIST_SYMBOL (action);
+         args = MPLIST_NEXT (action);
+         if (name == Minsert
+             && MPLIST_PLIST_P (args))
+           mplist_set (action, Msymbol, M_candidates);
+       }
+      else if (MPLIST_MTEXT_P (action) || MPLIST_PLIST_P (action))
+       {
+         action = mplist ();
+         mplist_push (action, Mplist, MPLIST_VAL (action_list));
+         mplist_push (action, Msymbol, M_candidates);
+         mplist_set (action_list, Mplist, action);
+         M17N_OBJECT_UNREF (action);
+       }
+    }
+  else if (MPLIST_MTEXT_P (action_list) || MPLIST_INTEGER_P (action_list))
+    {
+      action = mplist ();
+      mplist_push (action, MPLIST_KEY (action_list), MPLIST_VAL (action_list));
+      mplist_push (action, Msymbol, Minsert);
+      mplist_set (action_list, Mplist, action);
+      M17N_OBJECT_UNREF (action);
+    }
+  return action;
+}
+
+/* Perform list of actions in ACTION_LIST for the current input
+   context IC.  If unhandle action was not performed, return 0.
+   Otherwise, return -1.  */
+
+static int
+take_action_list (MInputContext *ic, MPlist *action_list)
+{
+  MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+  MTextProperty *prop;
+
+  MPLIST_DO (action_list, action_list)
+    {
+      MPlist *action = regularize_action (action_list, ic_info);
+      MSymbol name;
+      MPlist *args;
+
+      if (! action)
+       continue;
+      name = MPLIST_SYMBOL (action);
+      args = MPLIST_NEXT (action);
+
+      MDEBUG_PRINT1 (" %s", MSYMBOL_NAME (name));
+      if (name == Minsert)
+       {
+         if (MPLIST_SYMBOL_P (args))
+           {
+             args = resolve_variable (ic_info, MPLIST_SYMBOL (args));
+             if (! MPLIST_MTEXT_P (args) && ! MPLIST_INTEGER_P (args))
+               continue;
+           }
+         if (MPLIST_MTEXT_P (args))
+           preedit_insert (ic, ic->cursor_pos, MPLIST_MTEXT (args), 0);
+         else                  /* MPLIST_INTEGER_P (args)) */
+           preedit_insert (ic, ic->cursor_pos, NULL, MPLIST_INTEGER (args));
+       }
+      else if (name == M_candidates)
+       {
+         MPlist *plist = get_candidate_list (ic_info, args);
+         MPlist *pl;
+         int len;
+
+         if (! plist)
+           continue;
+         if (MPLIST_PLIST_P (plist) && MPLIST_TAIL_P (plist))
+           {
+             M17N_OBJECT_UNREF (plist);
+             continue;
+           }
+         if (MPLIST_MTEXT_P (plist))
+           {
+             preedit_insert (ic, ic->cursor_pos, NULL,
+                             mtext_ref_char (MPLIST_MTEXT (plist), 0));
+             len = 1;
+           }
+         else
+           {
+             MText * mt = MPLIST_MTEXT (MPLIST_PLIST (plist));
+
+             preedit_insert (ic, ic->cursor_pos, mt, 0);
+             len = mtext_nchars (mt);
+           }
+         pl = mplist_copy (plist);
+         M17N_OBJECT_UNREF (plist);
+         mtext_put_prop (ic->preedit,
+                         ic->cursor_pos - len, ic->cursor_pos,
+                         Mcandidate_list, pl);
+         M17N_OBJECT_UNREF (pl);
+         mtext_put_prop (ic->preedit,
+                         ic->cursor_pos - len, ic->cursor_pos,
+                         Mcandidate_index, (void *) 0);
+       }
+      else if (name == Mselect)
+       {
+         int start, end;
+         int code, idx, gindex;
+         int pos = ic->cursor_pos;
+         MPlist *group;
+         int idx_decided = 0;
+
+         if (pos == 0
+             || ! (prop = mtext_get_property (ic->preedit, pos - 1,
+                                              Mcandidate_list)))
+           continue;
+         idx = (int) mtext_get_prop (ic->preedit, pos - 1, Mcandidate_index);
+         group = find_candidates_group (mtext_property_value (prop), idx,
+                                        &start, &end, &gindex);
+         if (MPLIST_SYMBOL_P (args))
+           {
+             code = marker_code (MPLIST_SYMBOL (args), 0);
+             if (code < 0)
+               {
+                 args = resolve_variable (ic_info, MPLIST_SYMBOL (args));
+                 if (! MPLIST_INTEGER_P (args))
+                   continue;
+                 idx = start + MPLIST_INTEGER (args);
+                 if (idx < start || idx >= end)
+                   continue;
+                 idx_decided = 1;
+               }                 
+           }
+         else
+           code = -1;
+
+         if (code != '[' && code != ']')
+           {
+             if (! idx_decided)
+               idx = (start
+                      + (code >= 0
+                         ? new_index (NULL, ic->candidate_index - start,
+                                      end - start - 1, MPLIST_SYMBOL (args),
+                                      NULL)
+                         : MPLIST_INTEGER (args)));
+             if (idx < 0)
+               {
+                 find_candidates_group (mtext_property_value (prop), -1,
+                                        NULL, &end, NULL);
+                 idx = end - 1;
+               }
+             else if (idx >= end
+                      && MPLIST_TAIL_P (MPLIST_NEXT (group)))
+               idx = 0;
+           }
+         else
+           {
+             int ingroup_index = idx - start;
+             int len;
+
+             group = mtext_property_value (prop);
+             len = mplist_length (group);
+             if (code == '[')
+               {
+                 gindex--;
+                 if (gindex < 0)
+                   gindex = len - 1;;
+               }
+             else
+               {
+                 gindex++;
+                 if (gindex >= len)
+                   gindex = 0;
+               }
+             for (idx = 0; gindex > 0; gindex--, group = MPLIST_NEXT (group))
+               idx += (MPLIST_MTEXT_P (group)
+                       ? mtext_nchars (MPLIST_MTEXT (group))
+                       : mplist_length (MPLIST_PLIST (group)));
+             len = (MPLIST_MTEXT_P (group)
+                    ? mtext_nchars (MPLIST_MTEXT (group))
+                    : mplist_length (MPLIST_PLIST (group)));
+             if (ingroup_index >= len)
+               ingroup_index = len - 1;
+             idx += ingroup_index;
+           }
+         update_candidate (ic, prop, idx);
+         MDEBUG_PRINT1 ("(%d)", idx);
+       }
+      else if (name == Mshow)
+       ic->candidate_show = 1;
+      else if (name == Mhide)
+       ic->candidate_show = 0;
+      else if (name == Mdelete)
+       {
+         int len = mtext_nchars (ic->preedit);
+         int pos;
+         int to;
+
+         if (MPLIST_SYMBOL_P (args)
+             && surrounding_pos (MPLIST_SYMBOL (args), &pos))
+           {
+             to = ic->cursor_pos + pos;
+             if (to < 0)
+               {
+                 delete_surrounding_text (ic, to);
+                 to = 0;
+               }
+             else if (to > len)
+               {
+                 delete_surrounding_text (ic, to - len);
+                 to = len;
+               }
+           }
+         else
+           {
+             to = (MPLIST_SYMBOL_P (args)
+                   ? new_index (ic, ic->cursor_pos, len, MPLIST_SYMBOL (args),
+                                ic->preedit)
+                   : MPLIST_INTEGER (args));
+             if (to < 0)
+               to = 0;
+             else if (to > len)
+               to = len;
+             pos = to - ic->cursor_pos;
+           }
+         MDEBUG_PRINT1 ("(%d)", pos);
+         if (to < ic->cursor_pos)
+           preedit_delete (ic, to, ic->cursor_pos);
+         else if (to > ic->cursor_pos)
+           preedit_delete (ic, ic->cursor_pos, to);
+       }
+      else if (name == Mmove)
+       {
+         int len = mtext_nchars (ic->preedit);
+         int pos
+           = (MPLIST_SYMBOL_P (args)
+              ? new_index (ic, ic->cursor_pos, len, MPLIST_SYMBOL (args),
+                           ic->preedit)
+              : MPLIST_INTEGER (args));
+
+         if (pos < 0)
+           pos = 0;
+         else if (pos > len)
+           pos = len;
+         if (pos != ic->cursor_pos)
+           {
+             ic->cursor_pos = pos;
+             ic->preedit_changed = 1;
+           }
+         MDEBUG_PRINT1 ("(%d)", ic->cursor_pos);
+       }
+      else if (name == Mmark)
+       {
+         int code = marker_code (MPLIST_SYMBOL (args), 0);
+
+         if (code < 0)
+           {
+             mplist_put (ic_info->markers, MPLIST_SYMBOL (args),
+                         (void *) ic->cursor_pos);
+             MDEBUG_PRINT1 ("(%d)", ic->cursor_pos);
+           }
+       }
+      else if (name == Mpushback)
+       {
+         if (MPLIST_INTEGER_P (args) || MPLIST_SYMBOL_P (args))
+           {
+             int num;
+
+             if (MPLIST_SYMBOL_P (args))
+               {
+                 args = resolve_variable (ic_info, MPLIST_SYMBOL (args));
+                 if (MPLIST_INTEGER_P (args))
+                   num = MPLIST_INTEGER (args);
+                 else
+                   num = 0;
+               }
+             else
+               num = MPLIST_INTEGER (args);
+
+             if (num > 0)
+               ic_info->key_head -= num;
+             else if (num == 0)
+               ic_info->key_head = 0;
+             else
+               ic_info->key_head = - num;
+             if (ic_info->key_head > ic_info->used)
+               ic_info->key_head = ic_info->used;
+           }
+         else if (MPLIST_MTEXT_P (args))
+           {
+             MText *mt = MPLIST_MTEXT (args);
+             int i, len = mtext_nchars (mt);
+             MSymbol key;
+
+             ic_info->key_head--;
+             for (i = 0; i < len; i++)
+               {
+                 key = one_char_symbol[MTEXT_DATA (mt)[i]];
+                 if (ic_info->key_head + i < ic_info->used)
+                   ic_info->keys[ic_info->key_head + i] = key;
+                 else
+                   MLIST_APPEND1 (ic_info, keys, key, MERROR_IM);
+               }
+           }
+         else
+           {
+             MPlist *plist = MPLIST_PLIST (args), *pl;
+             int i = 0;
+             MSymbol key;
+
+             ic_info->key_head--;
+
+             MPLIST_DO (pl, plist)
+               {
+                 key = MPLIST_SYMBOL (pl);
+                 if (ic_info->key_head < ic_info->used)
+                   ic_info->keys[ic_info->key_head + i] = key;
+                 else
+                   MLIST_APPEND1 (ic_info, keys, key, MERROR_IM);
+                 i++;
+               }
+           }
+       }
+      else if (name == Mpop)
+       {
+         if (ic_info->key_head < ic_info->used)
+           MLIST_DELETE1 (ic_info, keys, ic_info->key_head, 1);
+       }
+      else if (name == Mcall)
+       {
+         MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
+         MIMExternalFunc func = NULL;
+         MSymbol module, func_name;
+         MPlist *func_args, *val;
+         int ret = 0;
+
+         module = MPLIST_SYMBOL (args);
+         args = MPLIST_NEXT (args);
+         func_name = MPLIST_SYMBOL (args);
+
+         if (im_info->externals)
+           {
+             MIMExternalModule *external
+               = (MIMExternalModule *) mplist_get (im_info->externals,
+                                                   module);
+             if (external)
+               func = ((MIMExternalFunc)
+                       mplist_get_func (external->func_list, func_name));
+           }
+         if (! func)
+           continue;
+         func_args = mplist ();
+         mplist_add (func_args, Mt, ic);
+         MPLIST_DO (args, MPLIST_NEXT (args))
+           {
+             int code;
+
+             if (MPLIST_KEY (args) == Msymbol
+                 && MPLIST_KEY (args) != Mnil
+                 && (code = marker_code (MPLIST_SYMBOL (args), 0)) >= 0)
+               {
+                 code = new_index (ic, ic->cursor_pos, 
+                                   mtext_nchars (ic->preedit),
+                                   MPLIST_SYMBOL (args), ic->preedit);
+                 mplist_add (func_args, Minteger, (void *) code);
+               }
+             else
+               mplist_add (func_args, MPLIST_KEY (args), MPLIST_VAL (args));
+           }
+         val = (func) (func_args);
+         M17N_OBJECT_UNREF (func_args);
+         if (val && ! MPLIST_TAIL_P (val))
+           ret = take_action_list (ic, val);
+         M17N_OBJECT_UNREF (val);
+         if (ret < 0)
+           return ret;
+       }
+      else if (name == Mshift)
+       {
+         shift_state (ic, MPLIST_SYMBOL (args));
+       }
+      else if (name == Mundo)
+       {
+         int intarg = (MPLIST_TAIL_P (args)
+                       ? ic_info->used - 2
+                       : integer_value (ic, args, 0));
+
+         mtext_reset (ic->preedit);
+         mtext_reset (ic_info->preedit_saved);
+         mtext_reset (ic->produced);
+         M17N_OBJECT_UNREF (ic_info->vars);
+         ic_info->vars = mplist_copy (ic_info->vars_saved);
+         ic->cursor_pos = ic_info->state_pos = 0;
+         ic_info->state_key_head = ic_info->key_head
+           = ic_info->commit_key_head = 0;
+
+         shift_state (ic, Mnil);
+         if (intarg < 0)
+           {
+             if (MPLIST_TAIL_P (args))
+               {
+                 ic_info->used = 0;
+                 return -1;
+               }
+             ic_info->used += intarg;
+           }
+         else
+           ic_info->used = intarg;
+         break;
+       }
+      else if (name == Mset || name == Madd || name == Msub
+              || name == Mmul || name == Mdiv)
+       {
+         MSymbol sym = MPLIST_SYMBOL (args);
+         MPlist *value = resolve_variable (ic_info, sym);
+         int val1, val2;
+         char *op;
+
+         val1 = MPLIST_INTEGER (value);
+         args = MPLIST_NEXT (args);
+         val2 = resolve_expression (ic, args);
+         if (name == Mset)
+           val1 = val2, op = "=";
+         else if (name == Madd)
+           val1 += val2, op = "+=";
+         else if (name == Msub)
+           val1 -= val2, op = "-=";
+         else if (name == Mmul)
+           val1 *= val2, op = "*=";
+         else
+           val1 /= val2, op = "/=";
+         MDEBUG_PRINT4 ("(%s %s 0x%X(%d))",
+                        MSYMBOL_NAME (sym), op, val1, val1);
+         mplist_set (value, Minteger, (void *) val1);
+       }
+      else if (name == Mequal || name == Mless || name == Mgreater
+              || name == Mless_equal || name == Mgreater_equal)
+       {
+         int val1, val2;
+         MPlist *actions1, *actions2;
+         int ret = 0;
+
+         val1 = resolve_expression (ic, args);
+         args = MPLIST_NEXT (args);
+         val2 = resolve_expression (ic, args);
+         args = MPLIST_NEXT (args);
+         actions1 = MPLIST_PLIST (args);
+         args = MPLIST_NEXT (args);
+         if (MPLIST_TAIL_P (args))
+           actions2 = NULL;
+         else
+           actions2 = MPLIST_PLIST (args);
+         MDEBUG_PRINT3 ("(%d %s %d)? ", val1, MSYMBOL_NAME (name), val2);
+         if (name == Mequal ? val1 == val2
+             : name == Mless ? val1 < val2
+             : name == Mgreater ? val1 > val2
+             : name == Mless_equal ? val1 <= val2
+             : val1 >= val2)
+           {
+             MDEBUG_PRINT ("ok");
+             ret = take_action_list (ic, actions1);
+           }
+         else
+           {
+             MDEBUG_PRINT ("no");
+             if (actions2)
+               ret = take_action_list (ic, actions2);
+           }
+         if (ret < 0)
+           return ret;
+       }
+      else if (name == Mcond)
+       {
+         int idx = 0;
+
+         MPLIST_DO (args, args)
+           {
+             MPlist *cond;
+
+             idx++;
+             if (! MPLIST_PLIST (args))
+               continue;
+             cond = MPLIST_PLIST (args);
+             if (resolve_expression (ic, cond) != 0)
+               {
+                 MDEBUG_PRINT1 ("(%dth)", idx);
+                 if (take_action_list (ic, MPLIST_NEXT (cond)) < 0)
+                   return -1;;
+                 break;
+               }
+           }
+       }
+      else if (name == Mcommit)
+       {
+         preedit_commit (ic, 0);
+       }
+      else if (name == Munhandle)
+       {
+         preedit_commit (ic, 0);
+         return -1;
+       }
+      else
+       {
+         MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
+         MPlist *actions;
+
+         if (im_info->macros
+             && (actions = mplist_get (im_info->macros, name)))
+           {
+             if (take_action_list (ic, actions) < 0)
+               return -1;
+           };
+       }
+    }
+  return 0;
+}
+
+
+/* Handle the input key KEY in the current state and map specified in
+   the input context IC.  If KEY is handled correctly, return 0.
+   Otherwise, return -1.  */
+
+static int
+handle_key (MInputContext *ic)
+{
+  MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
+  MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+  MIMMap *map = ic_info->map;
+  MIMMap *submap = NULL;
+  MSymbol key = ic_info->keys[ic_info->key_head];
+  MSymbol alias = Mnil;
+  int i;
+
+  if (ic_info->state_hook)
+    {
+      MDEBUG_PRINT1 ("  [IM] [%s] init-actions:",
+                    MSYMBOL_NAME (ic_info->state->name));
+      take_action_list (ic, ic_info->state_hook);
+      ic_info->state_hook = NULL;
+    }
+
+  MDEBUG_PRINT2 ("  [IM] [%s] handle `%s'", 
+                MSYMBOL_NAME (ic_info->state->name), msymbol_name (key));
+
+  if (map->submaps)
+    {
+      submap = mplist_get (map->submaps, key);
+      alias = key;
+      while (! submap
+            && (alias = msymbol_get (alias, M_key_alias))
+            && alias != key)
+       submap = mplist_get (map->submaps, alias);
+    }
+
+  if (submap)
+    {
+      if (! alias || alias == key)
+       MDEBUG_PRINT (" submap-found");
+      else
+       MDEBUG_PRINT1 (" submap-found (by alias `%s')", MSYMBOL_NAME (alias));
+      mtext_cpy (ic->preedit, ic_info->preedit_saved);
+      ic->preedit_changed = 1;
+      ic->cursor_pos = ic_info->state_pos;
+      ic_info->key_head++;
+      ic_info->map = map = submap;
+      if (map->map_actions)
+       {
+         MDEBUG_PRINT (" map-actions:");
+         if (take_action_list (ic, map->map_actions) < 0)
+           {
+             MDEBUG_PRINT ("\n");
+             return -1;
+           }
+       }
+      else if (map->submaps)
+       {
+         for (i = ic_info->state_key_head; i < ic_info->key_head; i++)
+           {
+             MSymbol key = ic_info->keys[i];
+             char *name = msymbol_name (key);
+
+             if (! name[0] || ! name[1])
+               mtext_ins_char (ic->preedit, ic->cursor_pos++, name[0], 1);
+           }
+       }
+
+      /* If this is the terminal map or we have shifted to another
+        state, perform branch actions (if any).  */
+      if (! map->submaps || map != ic_info->map)
+       {
+         if (map->branch_actions)
+           {
+             MDEBUG_PRINT (" branch-actions:");
+             if (take_action_list (ic, map->branch_actions) < 0)
+               {
+                 MDEBUG_PRINT ("\n");
+                 return -1;
+               }
+           }
+         /* If MAP is still not the root map, shift to the current
+            state.  */
+         if (ic_info->map != ic_info->state->map)
+           shift_state (ic, ic_info->state->name);
+       }
+    }
+  else
+    {
+      /* MAP can not handle KEY.  */
+
+      /* Perform branch actions if any.  */
+      if (map->branch_actions)
+       {
+         MDEBUG_PRINT (" branch-actions:");
+         if (take_action_list (ic, map->branch_actions) < 0)
+           {
+             MDEBUG_PRINT ("\n");
+             return -1;
+           }
+       }
+
+      if (map == ic_info->map)
+       {
+         /* The above branch actions didn't change the state.  */
+
+         /* If MAP is the root map of the initial state, and there
+            still exist an unhandled key, it means that the current
+            input method can not handle it.  */
+         if (map == ((MIMState *) MPLIST_VAL (im_info->states))->map
+             && ic_info->key_head < ic_info->used)
+           {
+             MDEBUG_PRINT (" unhandled\n");
+             ic_info->state_hook = map->map_actions;
+             return -1;
+           }
+
+         if (map != ic_info->state->map)
+           {
+             /* MAP is not the root map.  Shift to the root map of the
+                current state. */
+             shift_state (ic, ic_info->state->name);
+           }
+         else if (! map->branch_actions)
+           {
+             /* MAP is the root map without any default branch
+                actions.  Shift to the initial state.  */
+             shift_state (ic, Mnil);
+           }
+       }
+    }
+  MDEBUG_PRINT ("\n");
+  return 0;
+}
+
+/* Initialize IC->ic_info.  */
+
+static void
+init_ic_info (MInputContext *ic)
+{
+  MInputMethodInfo *im_info = ic->im->info;
+  MInputContextInfo *ic_info = ic->info;
+  MPlist *plist;
+  
+  MLIST_INIT1 (ic_info, keys, 8);;
+
+  ic_info->markers = mplist ();
+
+  ic_info->vars = mplist ();
+  if (im_info->configured_vars)
+    MPLIST_DO (plist, im_info->configured_vars)
+      {
+       MPlist *pl = MPLIST_PLIST (plist);
+       MSymbol name = MPLIST_SYMBOL (pl);
+
+       pl = MPLIST_NEXT (MPLIST_NEXT (MPLIST_NEXT (pl)));
+       if (MPLIST_KEY (pl) != Mt)
+         {
+           MPlist *p = mplist ();
+
+           mplist_push (ic_info->vars, Mplist, p);
+           M17N_OBJECT_UNREF (p);
+           mplist_add (p, Msymbol, name);
+           mplist_add (p, MPLIST_KEY (pl), MPLIST_VAL (pl));
+         }
+      }
+  ic_info->vars_saved = mplist_copy (ic_info->vars);
+
+  if (im_info->externals)
+    {
+      MPlist *func_args = mplist (), *plist;
+
+      mplist_add (func_args, Mt, ic);
+      MPLIST_DO (plist, im_info->externals)
+       {
+         MIMExternalModule *external = MPLIST_VAL (plist);
+         MIMExternalFunc func
+           = (MIMExternalFunc) mplist_get_func (external->func_list, Minit);
+
+         if (func)
+           (func) (func_args);
+       }
+      M17N_OBJECT_UNREF (func_args);
+    }
+
+  ic_info->preedit_saved = mtext ();
+  ic_info->tick = im_info->tick;
+}
+
+/* Finalize IC->ic_info.  */
+
+static void
+fini_ic_info (MInputContext *ic)
+{
+  MInputMethodInfo *im_info = ic->im->info;
+  MInputContextInfo *ic_info = ic->info;
+
+  if (im_info->externals)
+    {
+      MPlist *func_args = mplist (), *plist;
+
+      mplist_add (func_args, Mt, ic);
+      MPLIST_DO (plist, im_info->externals)
+       {
+         MIMExternalModule *external = MPLIST_VAL (plist);
+         MIMExternalFunc func
+           = (MIMExternalFunc) mplist_get_func (external->func_list, Mfini);
+
+         if (func)
+           (func) (func_args);
+       }
+      M17N_OBJECT_UNREF (func_args);
+    }
+
+  MLIST_FREE1 (ic_info, keys);
+  M17N_OBJECT_UNREF (ic_info->preedit_saved);
+  M17N_OBJECT_UNREF (ic_info->markers);
+  M17N_OBJECT_UNREF (ic_info->vars);
+  M17N_OBJECT_UNREF (ic_info->vars_saved);
+  M17N_OBJECT_UNREF (ic_info->preceding_text);
+  M17N_OBJECT_UNREF (ic_info->following_text);
+
+  memset (ic_info, 0, sizeof (MInputContextInfo));
+}
+
+static void
+re_init_ic (MInputContext *ic, int reload)
+{
+  MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
+  MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+  int status_changed, preedit_changed, cursor_pos_changed, candidates_changed;
+
+  status_changed = ic_info->state != (MIMState *) MPLIST_VAL (im_info->states);
+  preedit_changed = mtext_nchars (ic->preedit) > 0;
+  cursor_pos_changed = ic->cursor_pos > 0;
+  candidates_changed = 0;
+  if (ic->candidate_list)
+    {
+      candidates_changed |= MINPUT_CANDIDATES_LIST_CHANGED;
+      M17N_OBJECT_UNREF (ic->candidate_list);
+      ic->candidate_list = NULL;
+    }
+  if (ic->candidate_show)
+    {
+      candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;
+      ic->candidate_show = 0;
+    }
+  if (ic->candidate_index > 0)
+    {
+      candidates_changed |= MINPUT_CANDIDATES_INDEX_CHANGED;
+      ic->candidate_index = 0;
+      ic->candidate_from = ic->candidate_to = 0;
+    }
+  if (mtext_nchars (ic->produced) > 0)
+    mtext_reset (ic->produced);
+  if (mtext_nchars (ic->preedit) > 0)
+    mtext_reset (ic->preedit);
+  ic->cursor_pos = 0;
+  M17N_OBJECT_UNREF (ic->plist);
+  ic->plist = mplist ();
+
+  fini_ic_info (ic);
+  if (reload)
+    reload_im_info (im_info);
+  if (! im_info->states)
+    {
+      struct MIMState *state;
+
+      M17N_OBJECT (state, free_state, MERROR_IM);
+      state->name = msymbol ("init");
+      state->title = mtext__from_data ("ERROR!", 6, MTEXT_FORMAT_US_ASCII, 0);
+      MSTRUCT_CALLOC (state->map, MERROR_IM);
+      im_info->states = mplist ();
+      mplist_add (im_info->states, state->name, state);
+    }
+  init_ic_info (ic);
+  shift_state (ic, Mnil);
+
+  ic->status_changed = status_changed;
+  ic->preedit_changed = preedit_changed;
+  ic->cursor_pos_changed = cursor_pos_changed;
+  ic->candidates_changed = candidates_changed;
+}
+
+static void
+reset_ic (MInputContext *ic, MSymbol ignore)
+{
+  MDEBUG_PRINT ("\n  [IM] reset\n");
+  re_init_ic (ic, 0);
+}
+
+static int
+open_im (MInputMethod *im)
+{
+  MInputMethodInfo *im_info = get_im_info (im->language, im->name, Mnil, Mnil);
+
+  if (! im_info || ! im_info->states || MPLIST_LENGTH (im_info->states) == 0)
+    MERROR (MERROR_IM, -1);
+  im->info = im_info;
+
+  return 0;
+}
+
+static void
+close_im (MInputMethod *im)
+{
+  im->info = NULL;
+}
+
+static int
+create_ic (MInputContext *ic)
+{
+  MInputContextInfo *ic_info;
+
+  MSTRUCT_CALLOC (ic_info, MERROR_IM);
+  ic->info = ic_info;
+  init_ic_info (ic);
+  shift_state (ic, Mnil);
+  return 0;
+}
+
+static void
+destroy_ic (MInputContext *ic)
+{
+  fini_ic_info (ic);
+  free (ic->info);
+}
+
+static int
+check_reload (MInputContext *ic, MSymbol key)
+{
+  MInputMethodInfo *im_info = ic->im->info;
+  MPlist *plist = resolve_command (im_info->configured_cmds, Mat_reload);
+
+  if (! plist)
+    {
+      plist = resolve_command (global_info->configured_cmds, Mat_reload);
+      if (! plist)
+       return 0;
+    }
+  MPLIST_DO (plist, plist)
+    {
+      MSymbol this_key, alias;
+
+      if (MPLIST_MTEXT_P (plist))
+       {
+         MText *mt = MPLIST_MTEXT (plist);
+         int c = mtext_ref_char (mt, 0);
+
+         if (c >= 256)
+           continue;
+         this_key = one_char_symbol[c];
+       }
+      else
+       {
+         MPlist *pl = MPLIST_PLIST (plist);
+      
+         this_key = MPLIST_SYMBOL (pl);
+       }
+      alias = this_key;
+      while (alias != key 
+            && (alias = msymbol_get (alias, M_key_alias))
+            && alias != this_key);
+      if (alias == key)
+       break;
+    }
+  if (MPLIST_TAIL_P (plist))
+    return 0;
+
+  MDEBUG_PRINT ("\n  [IM] reload");
+  re_init_ic (ic, 1);
+  return 1;
+}
+
+
+/** Handle the input key KEY in the current state and map of IC->info.
+    If KEY is handled but no text is produced, return 0, otherwise
+    return 1.
+
+    Ignore ARG.  */
+
+static int
+filter (MInputContext *ic, MSymbol key, void *arg)
+{
+  MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
+  MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+  int i = 0;
+
+  if (check_reload (ic, key))
+    return 0;
+
+  if (! ic_info->state)
+    {
+      ic_info->key_unhandled = 1;
+      return 0;
+    }
+  mtext_reset (ic->produced);
+  ic->status_changed = ic->preedit_changed = ic->candidates_changed = 0;
+  M17N_OBJECT_UNREF (ic_info->preceding_text);
+  M17N_OBJECT_UNREF (ic_info->following_text);
+  ic_info->preceding_text = ic_info->following_text = NULL;
+  MLIST_APPEND1 (ic_info, keys, key, MERROR_IM);
+  ic_info->key_unhandled = 0;
+
+  do {
+    MPlist *candidate_list = ic->candidate_list;
+    int candidate_index = ic->candidate_index;
+    int candidate_show = ic->candidate_show;
+    MTextProperty *prop;
+    int result = handle_key (ic);
+
+    if (ic->candidate_list)
+      {
+       M17N_OBJECT_UNREF (ic->candidate_list);
+       ic->candidate_list = NULL;
+      }
+    if (ic->cursor_pos > 0
+       && (prop = mtext_get_property (ic->preedit, ic->cursor_pos - 1,
+                                      Mcandidate_list)))
+      {
+       ic->candidate_list = mtext_property_value (prop);
+       M17N_OBJECT_REF (ic->candidate_list);
+       ic->candidate_index
+         = (int) mtext_get_prop (ic->preedit, ic->cursor_pos - 1,
+                                 Mcandidate_index);
+       ic->candidate_from = mtext_property_start (prop);
+       ic->candidate_to = mtext_property_end (prop);
+      }
+    if (candidate_list != ic->candidate_list)
+      ic->candidates_changed |= MINPUT_CANDIDATES_LIST_CHANGED;
+    if (candidate_index != ic->candidate_index)
+      ic->candidates_changed |= MINPUT_CANDIDATES_INDEX_CHANGED;
+    if (candidate_show != ic->candidate_show)
+      ic->candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;    
+
+    if (result < 0)
+      {
+       /* KEY was not handled.  Delete it from the current key sequence.  */
+       if (ic_info->used > 0)
+         {
+           memmove (ic_info->keys, ic_info->keys + 1,
+                    sizeof (int) * (ic_info->used - 1));
+           ic_info->used--;
+           if (ic_info->state_key_head > 0)
+             ic_info->state_key_head--;
+           if (ic_info->commit_key_head > 0)
+             ic_info->commit_key_head--;             
+         }
+       /* This forces returning 1.  */
+       ic_info->key_unhandled = 1;
+       break;
+      }
+    if (i++ == 100)
+      {
+       mdebug_hook ();
+       reset_ic (ic, Mnil);
+       ic_info->key_unhandled = 1;
+       break;
+      }
+    /* Break the loop if all keys were handled.  */
+  } while (ic_info->key_head < ic_info->used);
+
+  /* If the current map is the root of the initial state, we should
+     produce any preedit text in ic->produced.  */
+  if (ic_info->map == ((MIMState *) MPLIST_VAL (im_info->states))->map)
+    preedit_commit (ic, 1);
+
+  if (mtext_nchars (ic->produced) > 0)
+    {
+      if (MDEBUG_FLAG ())
+       {
+         MDEBUG_PRINT1 ("\n  [IM] [%s] (produced",
+                        MSYMBOL_NAME (ic_info->state->name));
+         for (i = 0; i < mtext_nchars (ic->produced); i++)
+           MDEBUG_PRINT1 (" U+%04X", mtext_ref_char (ic->produced, i));
+         MDEBUG_PRINT (")");
+       }
+
+      mtext_put_prop (ic->produced, 0, mtext_nchars (ic->produced),
+                     Mlanguage, ic->im->language);
+    }
+  if (ic_info->commit_key_head > 0)
+    {
+      memmove (ic_info->keys, ic_info->keys + ic_info->commit_key_head,
+              sizeof (int) * (ic_info->used - ic_info->commit_key_head));
+      ic_info->used -= ic_info->commit_key_head;
+      ic_info->key_head -= ic_info->commit_key_head;
+      ic_info->state_key_head -= ic_info->commit_key_head;
+      ic_info->commit_key_head = 0;
+    }
+  if (ic_info->key_unhandled)
+    {
+      ic_info->used = 0;
+      ic_info->key_head = ic_info->state_key_head
+       = ic_info->commit_key_head = 0;
+    }
+
+  return (! ic_info->key_unhandled && mtext_nchars (ic->produced) == 0);
+}
+
+
+/** Return 1 if the last event or key was not handled, otherwise
+    return 0.
+
+    There is no need of looking up because ic->produced should already
+    contain the produced text (if any).
+
+    Ignore KEY.  */
+
+static int
+lookup (MInputContext *ic, MSymbol key, void *arg, MText *mt)
+{
+  mtext_cat (mt, ic->produced);
+  mtext_reset (ic->produced);
+  return (((MInputContextInfo *) ic->info)->key_unhandled ? -1 : 0);
+}
+
+\f
+/* Input method command handler.  */
+
+/* List of all (global and local) commands. 
+   (LANG:(IM-NAME:(COMMAND ...) ...) ...) ...
+   COMMAND is CMD-NAME:(mtext:DESCRIPTION plist:KEYSEQ ...))
+   Global commands are stored as (t (t COMMAND ...))  */
+
+\f
+/* Input method variable handler.  */
+
+
+/* Support functions for mdebug_dump_im.  */
+
+static void
+dump_im_map (MPlist *map_list, int indent)
+{
+  char *prefix;
+  MSymbol key = MPLIST_KEY (map_list);
+  MIMMap *map = (MIMMap *) MPLIST_VAL (map_list);
+
+  prefix = (char *) alloca (indent + 1);
+  memset (prefix, 32, indent);
+  prefix[indent] = '\0';
+
+  fprintf (mdebug__output, "(\"%s\" ", msymbol_name (key));
+  if (map->map_actions)
+    mdebug_dump_plist (map->map_actions, indent + 2);
+  if (map->submaps)
+    {
+      MPLIST_DO (map_list, map->submaps)
+       {
+         fprintf (mdebug__output, "\n%s  ", prefix);
+         dump_im_map (map_list, indent + 2);
+       }
+    }
+  if (map->branch_actions)
+    {
+      fprintf (mdebug__output, "\n%s  (branch\n%s    ", prefix, prefix);
+      mdebug_dump_plist (map->branch_actions, indent + 4);
+      fprintf (mdebug__output, ")");      
+    }
+  fprintf (mdebug__output, ")");
+}
+
+
+static void
+dump_im_state (MIMState *state, int indent)
+{
+  char *prefix;
+  MPlist *map_list;
+
+  prefix = (char *) alloca (indent + 1);
+  memset (prefix, 32, indent);
+  prefix[indent] = '\0';
+
+  fprintf (mdebug__output, "(%s", msymbol_name (state->name));
+  if (state->map->submaps)
+    {
+      MPLIST_DO (map_list, state->map->submaps)
+       {
+         fprintf (mdebug__output, "\n%s  ", prefix);
+         dump_im_map (map_list, indent + 2);
+       }
+    }
+  fprintf (mdebug__output, ")");
+}
+
+\f
+
+int
+minput__init ()
+{
+  Minput_driver = msymbol ("input-driver");
+
+  Minput_preedit_start = msymbol ("input-preedit-start");
+  Minput_preedit_done = msymbol ("input-preedit-done");
+  Minput_preedit_draw = msymbol ("input-preedit-draw");
+  Minput_status_start = msymbol ("input-status-start");
+  Minput_status_done = msymbol ("input-status-done");
+  Minput_status_draw = msymbol ("input-status-draw");
+  Minput_candidates_start = msymbol ("input-candidates-start");
+  Minput_candidates_done = msymbol ("input-candidates-done");
+  Minput_candidates_draw = msymbol ("input-candidates-draw");
+  Minput_set_spot = msymbol ("input-set-spot");
+  Minput_focus_move = msymbol ("input-focus-move");
+  Minput_focus_in = msymbol ("input-focus-in");
+  Minput_focus_out = msymbol ("input-focus-out");
+  Minput_toggle = msymbol ("input-toggle");
+  Minput_reset = msymbol ("input-reset");
+  Minput_get_surrounding_text = msymbol ("input-get-surrounding-text");
+  Minput_delete_surrounding_text = msymbol ("input-delete-surrounding-text");
+  Mcustomized = msymbol ("customized");
+  Mconfigured = msymbol ("configured");
+  Minherited = msymbol ("inherited");
+
+  minput_default_driver.open_im = open_im;
+  minput_default_driver.close_im = close_im;
+  minput_default_driver.create_ic = create_ic;
+  minput_default_driver.destroy_ic = destroy_ic;
+  minput_default_driver.filter = filter;
+  minput_default_driver.lookup = lookup;
+  minput_default_driver.callback_list = mplist ();
+  mplist_put_func (minput_default_driver.callback_list, Minput_reset,
+                  M17N_FUNC (reset_ic));
+  minput_driver = &minput_default_driver;
+
+  fully_initialized = 0;
+  return 0;
+}
+
+void
+minput__fini ()
+{
+  if (fully_initialized)
+    {
+      free_im_list (im_info_list);
+      if (im_custom_list)
+       free_im_list (im_custom_list);
+      if (im_config_list)
+       free_im_list (im_config_list);
+      M17N_OBJECT_UNREF (load_im_info_keys);
+    }
+
+  M17N_OBJECT_UNREF (minput_default_driver.callback_list);
+  M17N_OBJECT_UNREF (minput_driver->callback_list);
+
+}
+
+MSymbol
+minput__char_to_key (int c)
+{
+  if (c < 0 || c >= 0x100)
+    return Mnil;
+
+  return one_char_symbol[c];
+}
+
+/*** @} */
+#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
+
+\f
+/* External API */
+
+/*** @addtogroup m17nInputMethod */
+/*** @{ */
+/*=*/
+
+/***en
+    @brief Symbol whose name is "input-method".
+ */
+/***ja
+    @brief "input-method" ¤ò̾Á°¤È¤·¤Æ»ý¤Ä¥·¥ó¥Ü¥ë.
+ */
+MSymbol Minput_method;
+
+/***en
+    @name Variables: Predefined symbols for callback commands.  */
+/***ja
+    @name ÊÑ¿ô¡§ ¥³¡¼¥ë¥Ð¥Ã¥¯¥³¥Þ¥ó¥ÉÍÑÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë.  */
+/*** @{ */ 
+/***en
+    These are the predefined symbols that are used as the @c COMMAND
+    argument of callback functions of an input method driver (see
+    #MInputDriver::callback_list).  
+
+    Most of them do not require extra argument nor return any value;
+    exceptions are these:
+
+    @b Minput_get_surrounding_text: When a callback function assigned for
+    this command is called, the first element of #MInputContext::plist
+    has key #Minteger and the value specifies which portion of the
+    surrounding text should be retrieved.  If the value is positive,
+    it specifies the number of characters following the current cursor
+    position.  If the value is negative, the absolute value specifies
+    the number of characters preceding the current cursor position.
+    If the value is zero, it means that the caller just wants to know
+    if the surrounding text is currently supported or not.
+
+    If the surrounding text is currently supported, the callback
+    function must set the key of this element to #Mtext and the value
+    to the retrieved M-text.  The length of the M-text may be shorter
+    than the requested number of characters, if the available text is
+    not that long.  The length can be zero in the worst case.  Or, the
+    length may be longer if an application thinks it is more efficient
+    to return that length.
+
+    If the surrounding text is not currently supported, the callback
+    function should return without changing the first element of
+    #MInputContext::plist.
+
+    @b Minput_delete_surrounding_text: When a callback function assigned
+    for this command is called, the first element of
+    #MInputContext::plist has key #Minteger and the value specifies
+    which portion of the surrounding text should be deleted in the
+    same way as the case of Minput_get_surrounding_text.  The callback
+    function must delete the specified text.  It should not alter
+    #MInputContext::plist.  */ 
+/***ja
+    ÆþÎϥ᥽¥Ã¥É¥É¥é¥¤¥Ð¤Î¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤Ë¤ª¤¤¤Æ @c COMMAND 
+    °ú¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ëÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë (#MInputDriver::callback_list »²¾È)¡£
+
+    ¤Û¤È¤ó¤É¤ÏÄɲäΰú¿ô¤òɬÍפȤ·¤Ê¤¤¤·ÃͤòÊÖ¤µ¤Ê¤¤¤¬¡¢°Ê²¼¤ÏÎã³°¤Ç¤¢¤ë¡£
+
+    Minput_get_surrounding_text: ¤³¤Î¥³¥Þ¥ó¥É¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿¥³¡¼¥ë¥Ð¥Ã
+    ¥¯´Ø¿ô¤¬¸Æ¤Ð¤ì¤¿ºÝ¤Ë¤Ï¡¢ #MInputContext::plist ¤ÎÂè°ìÍ×ÁǤϥ­¡¼¤È¤·
+    ¤Æ#Minteger ¤ò¤È¤ê¡¢¤½¤ÎÃͤϥµ¥é¥¦¥ó¥Ç¥£¥ó¥°¥Æ¥­¥¹¥È¤Î¤¦¤Á¤É¤ÎÉôʬ
+    ¤ò¼è¤Ã¤ÆÍè¤ë¤«¤ò»ØÄꤹ¤ë¡£Ãͤ¬Àµ¤Ç¤¢¤ì¤Ð¡¢¸½ºß¤Î¥«¡¼¥½¥ë°ÌÃ֤˳¤¯
+    ÃͤθĿôʬ¤Îʸ»ú¤ò¼è¤ë¡£Éé¤Ç¤¢¤ì¤Ð¡¢¥«¡¼¥½¥ë°ÌÃÖ¤ËÀè¹Ô¤¹¤ëÃͤÎÀäÂÐ
+    ÃÍʬ¤Îʸ»ú¤ò¼è¤ë¡£¸½ºß¥µ¥é¥¦¥ó¥É¥Æ¥­¥¹¥È¤¬¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤ë¤«¤É¤¦
+    ¤«¤òÃΤꤿ¤¤¤À¤±¤Ç¤¢¤ì¤Ð¡¢¤³¤ÎÃͤϥ¼¥í¤Ç¤âÎɤ¤¡£
+
+    ¥µ¥é¥¦¥ó¥Ç¥£¥ó¥°¥Æ¥­¥¹¥È¤¬¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤ì¤Ð¡¢¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤Ï
+    ¤³¤ÎÍ×ÁǤΥ­¡¼¤ò #Mtext ¤Ë¡¢Ãͤò¼è¤ê¹þ¤ó¤ÀM-text ¤ËÀßÄꤷ¤Ê¤¯¤Æ¤Ï¤Ê
+    ¤é¤Ê¤¤¡£¤â¤·¥Æ¥­¥¹¥È¤ÎŤµ¤¬½¼Ê¬¤Ç¤Ê¤±¤ì¤Ð¡¢¤³¤Î M-text ¤ÎŤµ¤ÏÍ×
+    µá¤µ¤ì¤Æ¤¤¤ëʸ»ú¿ô¤è¤êû¤¯¤ÆÎɤ¤¡£ºÇ°­¤Î¾ì¹ç 0 ¤Ç¤â¤è¤¤¤·¡¢¥¢¥×¥ê¥±¡¼
+    ¥·¥ç¥ó¦¤ÇɬÍפǸúΨŪ¤À¤È»×¤¨¤ÐŤ¯¤Æ¤âÎɤ¤¡£
+
+    ¥µ¥é¥¦¥ó¥Ç¥£¥ó¥°¥Æ¥­¥¹¥È¤¬¥µ¥Ý¡¼¥È¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¡¢¥³¡¼¥ë¥Ð¥Ã¥¯´Ø
+    ¿ô¤Ï #MInputContext::plist ¤ÎÂè°ìÍ×ÁǤòÊѹ¹¤·¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
+
+    Minput_delete_surrounding_text: ¤³¤Î¥³¥Þ¥ó¥É¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿¥³¡¼¥ë
+    ¥Ð¥Ã¥¯´Ø¿ô¤¬¸Æ¤Ð¤ì¤¿ºÝ¤Ë¤Ï¡¢#MInputContext::plist ¤ÎÂè°ìÍ×ÁǤϡ¢¥­¡¼
+    ¤È¤·¤Æ#Minteger ¤ò¤È¤ê¡¢ÃͤϺï½ü¤¹¤ë¤Ù¤­¥µ¥é¥¦¥ó¥Ç¥£¥ó¥°¥Æ¥­¥¹¥È¤ò
+    Minput_get_surrounding_text ¤ÈƱÍͤΤä¤êÊý¤Ç»ØÄꤹ¤ë¡£¥³¡¼¥ë¥Ð¥Ã¥¯
+    ´Ø¿ô¤Ï»ØÄꤵ¤ì¤¿¥Æ¥­¥¹¥È¤òºï½ü¤·¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤Þ¤¿
+    #MInputContext::plist ¤òÊѤ¨¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£  */ 
+MSymbol Minput_preedit_start;
+MSymbol Minput_preedit_done;
+MSymbol Minput_preedit_draw;
+MSymbol Minput_status_start;
+MSymbol Minput_status_done;
+MSymbol Minput_status_draw;
+MSymbol Minput_candidates_start;
+MSymbol Minput_candidates_done;
+MSymbol Minput_candidates_draw;
+MSymbol Minput_set_spot;
+MSymbol Minput_toggle;
+MSymbol Minput_reset;
+MSymbol Minput_get_surrounding_text;
+MSymbol Minput_delete_surrounding_text;
+/*** @} */
+
+/*=*/
+
+/***en
+    @name Variables: Predefined symbols for special input events.
+
+    These are the predefined symbols that are used as the @c KEY
+    argument of minput_filter ().  */ 
+/***ja
+    @name ÊÑ¿ô: ÆÃÊ̤ÊÆþÎÏ¥¤¥Ù¥ó¥ÈÍÑÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë.
+
+    minput_filter () ¤Î @c KEY °ú¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ëÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë¡£  */ 
+
+/*** @{ */ 
+/*=*/
+
+MSymbol Minput_focus_out;
+MSymbol Minput_focus_in;
+MSymbol Minput_focus_move;
+
+/*** @} */
+
+/*=*/
+/***en
+    @name Variables: Predefined symbols used in input method information.  */
+/***ja
+    @name ÊÑ¿ô: ÆþÎϥ᥽¥Ã¥É¾ðÊóÍÑÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë.  */
+/*** @{ */ 
+/*=*/
+/***en
+    These are the predefined symbols describing status of input method
+    command and variable, and are used in a return value of
+    minput_get_command () and minput_get_variable ().  */
+/***ja
+    ÆþÎϥ᥽¥Ã¥É¤Î¥³¥Þ¥ó¥É¤äÊÑ¿ô¤Î¾õÂÖ¤òɽ¤·¡¢minput_get_command () ¤È
+    minput_get_variable () ¤ÎÌá¤êÃͤȤ·¤ÆÍѤ¤¤é¤ì¤ëÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë¡£  */
+MSymbol Minherited;
+MSymbol Mcustomized;
+MSymbol Mconfigured;
+/*** @} */ 
+
+/*=*/
+
+/***en
+    @brief The default driver for internal input methods.
+
+    The variable #minput_default_driver is the default driver for
+    internal input methods.
+
+    The member MInputDriver::open_im () searches the m17n database for
+    an input method that matches the tag \< #Minput_method, $LANGUAGE,
+    $NAME\> and loads it.
+
+    The member MInputDriver::callback_list () is @c NULL.  Thus, it is
+    programmers responsibility to set it to a plist of proper callback
+    functions.  Otherwise, no feedback information (e.g. preedit text)
+    can be shown to users.
+
+    The macro M17N_INIT () sets the variable #minput_driver to the
+    pointer to this driver so that all internal input methods use it.
+
+    Therefore, unless @c minput_driver is set differently, the driver
+    dependent arguments $ARG of the functions whose name begins with
+    "minput_" are all ignored.  */
+/***ja
+    @brief ÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѥǥե©¥ë¥È¥É¥é¥¤¥Ð.
+
+    ÊÑ¿ô #minput_default_driver ¤ÏÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѤΥǥե©¥ë¥È¤Î¥É¥é¥¤¥Ð¤òɽ¤¹¡£
+
+    ¥á¥ó¥Ð MInputDriver::open_im () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é¥¿¥° 
+    \< #Minput_method, $LANGUAGE, $NAME\> 
+    ¤Ë¹çÃפ¹¤ëÆþÎϥ᥽¥Ã¥É¤òõ¤·¡¢¤½¤ì¤ò¥í¡¼¥É¤¹¤ë¡£
+
+    ¥á¥ó¥Ð MInputDriver::callback_list () ¤Ï @c NULL ¤Ç¤¢¤ê¡¢
+    ¤·¤¿¤¬¤Ã¤Æ¡¢¥×¥í¥°¥é¥Þ¦¤ÇÀÕǤ¤ò»ý¤Ã¤Æ Å¬Àڤʥ³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤Î plist
+    ¤ËÀßÄꤷ¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¤µ¤â¤Ê¤¤¤È¡¢preedit 
+    ¥Æ¥­¥¹¥È¤Ê¤É¤Î¥Õ¥£¡¼¥É¥Ð¥Ã¥¯¾ðÊ󤬥桼¥¶¤Ëɽ¼¨¤µ¤ì¤Ê¤¤¡£
+
+    ¥Þ¥¯¥í M17N_INIT () ¤ÏÊÑ¿ô #minput_driver 
+    ¤ò¤³¤Î¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤ËÀßÄꤷ¡¢Á´¤Æ¤ÎÆâÉôÆþÎϥ᥽¥Ã¥É¤¬¤³¤Î¥É¥é¥¤¥Ð¤ò»È¤¦¤è¤¦¤Ë¤¹¤ë¡£
+
+    ¤·¤¿¤¬¤Ã¤Æ¡¢@c minput_driver ¤¬¥Ç¥Õ¥©¥ë¥ÈÃͤΤޤޤǤ¢¤ì¤Ð¡¢minput_ 
+    ¤Ç»Ï¤Þ¤ë´Ø¿ô¤Î¥É¥é¥¤¥Ð¤Ë°Í¸¤¹¤ë°ú¿ô $ARG ¤Ï¤¹¤Ù¤Æ̵»ë¤µ¤ì¤ë¡£  */
+
+MInputDriver minput_default_driver;
+/*=*/
+
+/***en
+    @brief The driver for internal input methods.
+
+    The variable #minput_driver is a pointer to the input method
+    driver that is used by internal input methods.  The macro
+    M17N_INIT () initializes it to a pointer to #minput_default_driver
+    if <m17n<EM></EM>.h> is included.  */ 
+/***ja
+    @brief ÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѥɥ饤¥Ð.
+
+    ÊÑ¿ô #minput_driver ¤ÏÆâÉôÆþÎϥ᥽¥Ã¥É¤Ë¤è¤Ã¤Æ»ÈÍѤµ¤ì¤Æ¤¤¤ëÆþÎÏ¥á
+    ¥½¥Ã¥É¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¥Þ¥¯¥í M17N_INIT () ¤Ï¤³¤Î¥Ý¥¤¥ó
+    ¥¿¤ò#minput_default_driver (<m17n<EM></EM>.h> ¤¬ include ¤µ¤ì¤Æ¤¤¤ë
+    »þ) ¤Ë½é´ü²½¤¹¤ë¡£  */ 
+
+MInputDriver *minput_driver;
+
+/*=*/
+/***
+    The variable #Minput_driver is a symbol for a foreign input method.
+    See @ref foreign-input-method "foreign input method" for the detail.  */
+MSymbol Minput_driver;
+
+/*=*/
+
+/***en
+    @name Functions
+*/
+/***ja
+    @name ´Ø¿ô
+*/
+/*** @{ */
+
+/*=*/
+
+/***en
+    @brief Open an input method.
+
+    The minput_open_im () function opens an input method whose
+    language and name match $LANGUAGE and $NAME, and returns a pointer
+    to the input method object newly allocated.
+
+    This function at first decides a driver for the input method as
+    described below.
+
+    If $LANGUAGE is not #Mnil, the driver pointed by the variable
+    #minput_driver is used.
+
+    If $LANGUAGE is #Mnil and $NAME has the property #Minput_driver, the
+    driver pointed to by the property value is used to open the input
+    method.  If $NAME has no such a property, @c NULL is returned.
+
+    Then, the member MInputDriver::open_im () of the driver is
+    called.  
+
+    $ARG is set in the member @c arg of the structure MInputMethod so
+    that the driver can refer to it.  */
+/***ja
+    @brief ÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤¹¤ë.
+
+    ´Ø¿ô minput_open_im () ¤Ï¸À¸ì $LANGUAGE ¤È̾Á° $NAME 
+    ¤Ë¹çÃפ¹¤ëÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤·¡¢¿·¤¿¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿ÆþÎϥ᥽¥Ã¥É¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
+    
+    ¤³¤Î´Ø¿ô¤Ï¡¢¤Þ¤ºÆþÎϥ᥽¥Ã¥ÉÍѤΥɥ饤¥Ð¤ò°Ê²¼¤Î¤è¤¦¤Ë¤·¤Æ·èÄꤹ¤ë¡£
+
+    $LANGUAGE ¤¬ #Mnil ¤Ç¤Ê¤±¤ì¤Ð¡¢ÊÑ¿ô #minput_driver 
+    ¤Ç»Ø¤µ¤ì¤Æ¤¤¤ë¥É¥é¥¤¥Ð¤òÍѤ¤¤ë¡£
+
+    $LANGUAGE ¤¬ #Mnil ¤Ç¤¢¤ê¡¢$NAME ¤¬ #Minput_driver
+    ¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¾ì¹ç¤Ë¤Ï¡¢¤½¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤǻؤµ¤ì¤Æ¤¤¤ëÆþÎϥɥ饤¥Ð¤òÍѤ¤¤ÆÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤¹¤ë¡£
+    $NAME ¤Ë¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤¬Ìµ¤«¤Ã¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£
+
+    ¼¡¤¤¤Ç¡¢¥É¥é¥¤¥Ð¤Î¥á¥ó¥Ð MInputDriver::open_im () ¤¬¸Æ¤Ð¤ì¤ë¡£
+
+    $ARG ¤Ï¹½Â¤ÂΠMInputMethod ¤Î¥á¥ó¥Ð @c arg ¤ËÀßÄꤵ¤ì¡¢¥É¥é¥¤¥Ð¤«¤é»²¾È¤Ç¤­¤ë¡£
+
+    @latexonly \IPAlabel{minput_open} @endlatexonly
+
+*/
 
-         if (MPLIST_SYMBOL_P (args)
-             && (pos = surrounding_pos (MPLIST_SYMBOL (args))) != 0)
-           {
-             delete_surrounding_text (ic, pos);
-           }
-         else
-           {
-             to = (MPLIST_SYMBOL_P (args)
-                   ? new_index (ic, ic->cursor_pos, len, MPLIST_SYMBOL (args),
-                                ic->preedit)
-                   : MPLIST_INTEGER (args));
-             if (to < 0)
-               to = 0;
-             else if (to > len)
-               to = len;
-             MDEBUG_PRINT1 ("(%d)", to - ic->cursor_pos);
-             if (to < ic->cursor_pos)
-               preedit_delete (ic, to, ic->cursor_pos);
-             else if (to > ic->cursor_pos)
-               preedit_delete (ic, ic->cursor_pos, to);
-           }
-       }
-      else if (name == Mmove)
-       {
-         int len = mtext_nchars (ic->preedit);
-         int pos
-           = (MPLIST_SYMBOL_P (args)
-              ? new_index (ic, ic->cursor_pos, len, MPLIST_SYMBOL (args),
-                           ic->preedit)
-              : MPLIST_INTEGER (args));
+MInputMethod *
+minput_open_im (MSymbol language, MSymbol name, void *arg)
+{
+  MInputMethod *im;
+  MInputDriver *driver;
 
-         if (pos < 0)
-           pos = 0;
-         else if (pos > len)
-           pos = len;
-         if (pos != ic->cursor_pos)
-           {
-             ic->cursor_pos = pos;
-             ic->preedit_changed = 1;
-           }
-       }
-      else if (name == Mmark)
-       {
-         int code = marker_code (MPLIST_SYMBOL (args));
+  MINPUT__INIT ();
 
-         if (code < 0)
-           mplist_put (ic_info->markers, MPLIST_SYMBOL (args),
-                       (void *) ic->cursor_pos);
-       }
-      else if (name == Mpushback)
-       {
-         if (MPLIST_INTEGER_P (args))
-           {
-             int num = MPLIST_INTEGER (args);
+  MDEBUG_PRINT2 ("  [IM] opening (%s %s) ... ",
+                msymbol_name (language), msymbol_name (name));
+  if (language)
+    {
+      if (name == Mnil)
+       MERROR (MERROR_IM, NULL);
+      driver = minput_driver;
+    }
+  else
+    {
+      driver = (MInputDriver *) msymbol_get (name, Minput_driver);
+      if (! driver)
+       MERROR (MERROR_IM, NULL);
+    }
 
-             if (num > 0)
-               ic_info->key_head -= num;
-             else
-               ic_info->key_head = num;
-             if (ic_info->key_head > ic_info->used)
-               ic_info->key_head = ic_info->used;
-           }
-         else if (MPLIST_MTEXT_P (args))
-           {
-             MText *mt = MPLIST_MTEXT (args);
-             int i, len = mtext_nchars (mt);
-             MSymbol key;
+  MSTRUCT_CALLOC (im, MERROR_IM);
+  im->language = language;
+  im->name = name;
+  im->arg = arg;
+  im->driver = *driver;
+  if ((*im->driver.open_im) (im) < 0)
+    {
+      MDEBUG_PRINT (" failed\n");
+      free (im);
+      return NULL;
+    }
+  MDEBUG_PRINT (" ok\n");
+  return im;
+}
 
-             ic_info->key_head--;
-             for (i = 0; i < len; i++)
-               {
-                 key = one_char_symbol[MTEXT_DATA (mt)[i]];
-                 if (ic_info->key_head + i < ic_info->used)
-                   ic_info->keys[ic_info->key_head + i] = key;
-                 else
-                   MLIST_APPEND1 (ic_info, keys, key, MERROR_IM);
-               }
-           }
-         else
-           {
-             MPlist *plist = MPLIST_PLIST (args), *pl;
-             int i = 0;
-             MSymbol key;
+/*=*/
 
-             ic_info->key_head--;
+/***en
+    @brief Close an input method.
 
-             MPLIST_DO (pl, plist)
-               {
-                 key = MPLIST_SYMBOL (pl);
-                 if (ic_info->key_head < ic_info->used)
-                   ic_info->keys[ic_info->key_head + i] = key;
-                 else
-                   MLIST_APPEND1 (ic_info, keys, key, MERROR_IM);
-                 i++;
-               }
-           }
-       }
-      else if (name == Mcall)
-       {
-         MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
-         MIMExternalFunc func = NULL;
-         MSymbol module, func_name;
-         MPlist *func_args, *val;
-         int ret = 0;
+    The minput_close_im () function closes the input method $IM, which
+    must have been created by minput_open_im ().  */
 
-         module = MPLIST_SYMBOL (args);
-         args = MPLIST_NEXT (args);
-         func_name = MPLIST_SYMBOL (args);
+/***ja
+    @brief ÆþÎϥ᥽¥Ã¥É¤ò¥¯¥í¡¼¥º¤¹¤ë.
 
-         if (im_info->externals)
-           {
-             MIMExternalModule *external
-               = (MIMExternalModule *) mplist_get (im_info->externals,
-                                                   module);
-             if (external)
-               func = (MIMExternalFunc) mplist_get (external->func_list,
-                                                    func_name);
-           }
-         if (! func)
-           continue;
-         func_args = mplist ();
-         mplist_add (func_args, Mt, ic);
-         MPLIST_DO (args, MPLIST_NEXT (args))
-           {
-             int code;
+    ´Ø¿ô minput_close_im () ¤Ï¡¢ÆþÎϥ᥽¥Ã¥É $IM ¤ò¥¯¥í¡¼¥º¤¹¤ë¡£
+    ¤³¤ÎÆþÎϥ᥽¥Ã¥É $IM ¤Ï minput_open_im () ¤Ë¤è¤Ã¤Æºî¤é¤ì¤¿¤â¤Î¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£  */
 
-             if (MPLIST_KEY (args) == Msymbol
-                 && MPLIST_KEY (args) != Mnil
-                 && (code = marker_code (MPLIST_SYMBOL (args))) >= 0)
-               {
-                 code = new_index (ic, ic->cursor_pos, 
-                                   mtext_nchars (ic->preedit),
-                                   MPLIST_SYMBOL (args), ic->preedit);
-                 mplist_add (func_args, Minteger, (void *) code);
-               }
-             else
-               mplist_add (func_args, MPLIST_KEY (args), MPLIST_VAL (args));
-           }
-         val = (func) (func_args);
-         M17N_OBJECT_UNREF (func_args);
-         if (val && ! MPLIST_TAIL_P (val))
-           ret = take_action_list (ic, val);
-         M17N_OBJECT_UNREF (val);
-         if (ret < 0)
-           return ret;
-       }
-      else if (name == Mshift)
-       {
-         shift_state (ic, MPLIST_SYMBOL (args));
-       }
-      else if (name == Mundo)
-       {
-         int intarg = (MPLIST_TAIL_P (args)
-                       ? ic_info->used - 2
-                       : integer_value (ic, args, NULL, 0));
+void
+minput_close_im (MInputMethod *im)
+{
+  MDEBUG_PRINT2 ("  [IM] closing (%s %s) ... ",
+                msymbol_name (im->name), msymbol_name (im->language));
+  (*im->driver.close_im) (im);
+  free (im);
+  MDEBUG_PRINT (" done\n");
+}
 
-         mtext_reset (ic->preedit);
-         mtext_reset (ic_info->preedit_saved);
-         ic->cursor_pos = ic_info->state_pos = 0;
-         ic_info->state_key_head = ic_info->key_head = 0;
+/*=*/
 
-         if (intarg < 0)
-           ic_info->used += intarg;
-         else
-           ic_info->used = intarg;
-         shift_state (ic, Mnil);
-         break;
-       }
-      else if (name == Mset || name == Madd || name == Msub
-              || name == Mmul || name == Mdiv)
-       {
-         MSymbol sym = MPLIST_SYMBOL (args);
-         int val1, val2;
-         MPlist *value;
-         char *op;
+/***en
+    @brief Create an input context.
 
-         val1 = integer_value (ic, args, &value, 0);
-         args = MPLIST_NEXT (args);
-         val2 = resolve_expression (ic, args);
-         if (name == Mset)
-           val1 = val2, op = "=";
-         else if (name == Madd)
-           val1 += val2, op = "+=";
-         else if (name == Msub)
-           val1 -= val2, op = "-=";
-         else if (name == Mmul)
-           val1 *= val2, op = "*=";
-         else
-           val1 /= val2, op = "/=";
-         MDEBUG_PRINT4 ("(%s %s 0x%X(%d))",
-                        MSYMBOL_NAME (sym), op, val1, val1);
-         if (value)
-           mplist_set (value, Minteger, (void *) val1);
-       }
-      else if (name == Mequal || name == Mless || name == Mgreater
-              || name == Mless_equal || name == Mgreater_equal)
-       {
-         int val1, val2;
-         MPlist *actions1, *actions2;
-         int ret = 0;
+    The minput_create_ic () function creates an input context object
+    associated with input method $IM, and calls callback functions
+    corresponding to @b Minput_preedit_start, @b Minput_status_start, and
+    @b Minput_status_draw in this order.
 
-         val1 = resolve_expression (ic, args);
-         args = MPLIST_NEXT (args);
-         val2 = resolve_expression (ic, args);
-         args = MPLIST_NEXT (args);
-         actions1 = MPLIST_PLIST (args);
-         args = MPLIST_NEXT (args);
-         if (MPLIST_TAIL_P (args))
-           actions2 = NULL;
-         else
-           actions2 = MPLIST_PLIST (args);
-         MDEBUG_PRINT3 ("(%d %s %d)? ", val1, MSYMBOL_NAME (name), val2);
-         if (name == Mequal ? val1 == val2
-             : name == Mless ? val1 < val2
-             : name == Mgreater ? val1 > val2
-             : name == Mless_equal ? val1 <= val2
-             : val1 >= val2)
-           {
-             MDEBUG_PRINT ("ok");
-             ret = take_action_list (ic, actions1);
-           }
-         else
-           {
-             MDEBUG_PRINT ("no");
-             if (actions2)
-               ret = take_action_list (ic, actions2);
-           }
-         if (ret < 0)
-           return ret;
-       }
-      else if (name == Mcond)
-       {
-         int idx = 0;
+    @return
+    If an input context is successfully created, minput_create_ic ()
+    returns a pointer to it.  Otherwise it returns @c NULL.  */
+
+/***ja
+    @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤òÀ¸À®¤¹¤ë.
 
-         MPLIST_DO (args, args)
-           {
-             MPlist *cond;
+    ´Ø¿ô minput_create_ic () ¤ÏÆþÎϥ᥽¥Ã¥É $IM
+    ¤ËÂбþ¤¹¤ëÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®¤·¡¢
+    @b Minput_preedit_start, @b Minput_status_start, @b Minput_status_draw
+    ¤ËÂбþ¤¹¤ë¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤ò¤³¤Î½ç¤Ë¸Æ¤Ö¡£
 
-             idx++;
-             if (! MPLIST_PLIST (args))
-               continue;
-             cond = MPLIST_PLIST (args);
-             if (resolve_expression (ic, cond) != 0)
-               {
-                 MDEBUG_PRINT1 ("(%dth)", idx);
-                 if (take_action_list (ic, MPLIST_NEXT (cond)) < 0)
-                   return -1;;
-                 break;
-               }
-           }
-       }
-      else if (name == Mcommit)
-       {
-         preedit_commit (ic);
-       }
-      else if (name == Munhandle)
-       {
-         preedit_commit (ic);
-         return -1;
-       }
-      else
-       {
-         MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
-         MPlist *actions;
+    @return
+    ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤¬À¸À®¤µ¤ì¤¿¾ì¹ç¡¢minput_create_ic () 
+    ¤Ï¤½¤ÎÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£
+      */
 
-         if (im_info->macros
-             && (actions = mplist_get (im_info->macros, name)))
-           {
-             if (take_action_list (ic, actions) < 0)
-               return -1;
-           };
-       }
-    }
+MInputContext *
+minput_create_ic (MInputMethod *im, void *arg)
+{
+  MInputContext *ic;
 
-  prop = NULL;
-  if (ic->candidate_list)
+  MDEBUG_PRINT2 ("  [IM] creating context (%s %s) ... ",
+                msymbol_name (im->name), msymbol_name (im->language));
+  MSTRUCT_CALLOC (ic, MERROR_IM);
+  ic->im = im;
+  ic->arg = arg;
+  ic->preedit = mtext ();
+  ic->candidate_list = NULL;
+  ic->produced = mtext ();
+  ic->spot.x = ic->spot.y = 0;
+  ic->active = 1;
+  ic->plist = mplist ();
+  if ((*im->driver.create_ic) (ic) < 0)
     {
-      M17N_OBJECT_UNREF (ic->candidate_list);
-      ic->candidate_list = NULL;
+      MDEBUG_PRINT (" failed\n");
+      M17N_OBJECT_UNREF (ic->preedit);
+      M17N_OBJECT_UNREF (ic->produced);
+      M17N_OBJECT_UNREF (ic->plist);
+      free (ic);
+      return NULL;
+    };
+
+  if (im->driver.callback_list)
+    {
+      minput_callback (ic, Minput_preedit_start);
+      minput_callback (ic, Minput_status_start);
+      minput_callback (ic, Minput_status_draw);
     }
-  if (ic->cursor_pos > 0
-      && (prop = mtext_get_property (ic->preedit, ic->cursor_pos - 1,
-                                    Mcandidate_list)))
-    {
-      ic->candidate_list = mtext_property_value (prop);
-      M17N_OBJECT_REF (ic->candidate_list);
-      ic->candidate_index
-       = (int) mtext_get_prop (ic->preedit, ic->cursor_pos - 1,
-                               Mcandidate_index);
-      ic->candidate_from = mtext_property_start (prop);
-      ic->candidate_to = mtext_property_end (prop);
-    }
-
-  if (candidate_list != ic->candidate_list)
-    ic->candidates_changed |= MINPUT_CANDIDATES_LIST_CHANGED;
-  if (candidate_index != ic->candidate_index)
-    ic->candidates_changed |= MINPUT_CANDIDATES_INDEX_CHANGED;
-  if (candidate_show != ic->candidate_show)
-    ic->candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;    
-  return 0;
+
+  MDEBUG_PRINT (" ok\n");
+  return ic;
 }
 
+/*=*/
 
-/* Handle the input key KEY in the current state and map specified in
-   the input context IC.  If KEY is handled correctly, return 0.
-   Otherwise, return -1.  */
+/***en
+    @brief Destroy an input context.
 
-static int
-handle_key (MInputContext *ic)
-{
-  MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
-  MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
-  MIMMap *map = ic_info->map;
-  MIMMap *submap = NULL;
-  MSymbol key = ic_info->keys[ic_info->key_head];
-  MSymbol alias = Mnil;
-  int i;
+    The minput_destroy_ic () function destroys the input context $IC,
+    which must have been created by minput_create_ic ().  It calls
+    callback functions corresponding to @b Minput_preedit_done,
+    @b Minput_status_done, and @b Minput_candidates_done in this order.  */
+
+/***ja
+    @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤òÇ˲õ¤¹¤ë.
 
-  MDEBUG_PRINT2 ("  [IM] handle `%s' in state %s", 
-                msymbol_name (key), MSYMBOL_NAME (ic_info->state->name));
+    ´Ø¿ô minput_destroy_ic () ¤Ï¡¢ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤òÇ˲õ¤¹¤ë¡£
+    ¤³¤ÎÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Ï minput_create_ic () 
+    ¤Ë¤è¤Ã¤Æºî¤é¤ì¤¿¤â¤Î¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤³¤Î´Ø¿ô¤Ï 
+    @b Minput_preedit_done, @b Minput_status_done, @b Minput_candidates_done 
+    ¤ËÂбþ¤¹¤ë¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤ò¤³¤Î½ç¤Ë¸Æ¤Ö¡£
+  */
 
-  if (map->submaps)
+void
+minput_destroy_ic (MInputContext *ic)
+{
+  MDEBUG_PRINT2 ("  [IM] destroying context (%s %s) ... ",
+                msymbol_name (ic->im->name), msymbol_name (ic->im->language));
+  if (ic->im->driver.callback_list)
     {
-      submap = mplist_get (map->submaps, key);
-      if (! submap && (alias = msymbol_get (key, M_key_alias)) != Mnil)
-       submap = mplist_get (map->submaps, alias);
+      minput_callback (ic, Minput_preedit_done);
+      minput_callback (ic, Minput_status_done);
+      minput_callback (ic, Minput_candidates_done);
     }
+  (*ic->im->driver.destroy_ic) (ic);
+  M17N_OBJECT_UNREF (ic->preedit);
+  M17N_OBJECT_UNREF (ic->produced);
+  M17N_OBJECT_UNREF (ic->plist);
+  MDEBUG_PRINT (" done\n");
+  free (ic);
+}
 
-  if (submap)
-    {
-      if (alias == Mnil)
-       MDEBUG_PRINT (" submap-found");
-      else
-       MDEBUG_PRINT1 (" submap-found (by alias `%s')", MSYMBOL_NAME (alias));
-      mtext_cpy (ic->preedit, ic_info->preedit_saved);
-      ic->preedit_changed = 1;
-      ic->cursor_pos = ic_info->state_pos;
-      ic_info->key_head++;
-      ic_info->map = map = submap;
-      if (map->map_actions)
-       {
-         MDEBUG_PRINT (" map-actions:");
-         if (take_action_list (ic, map->map_actions) < 0)
-           {
-             MDEBUG_PRINT ("\n");
-             return -1;
-           }
-       }
-      else if (map->submaps)
-       {
-         for (i = ic_info->state_key_head; i < ic_info->key_head; i++)
-           {
-             MSymbol key = ic_info->keys[i];
-             char *name = msymbol_name (key);
+/*=*/
 
-             if (! name[0] || ! name[1])
-               mtext_ins_char (ic->preedit, ic->cursor_pos++, name[0], 1);
-           }
-       }
+/***en
+    @brief Filter an input key.
 
-      /* If this is the terminal map or we have shifted to another
-        state, perform branch actions (if any).  */
-      if (! map->submaps || map != ic_info->map)
-       {
-         if (map->branch_actions)
-           {
-             MDEBUG_PRINT (" branch-actions:");
-             if (take_action_list (ic, map->branch_actions) < 0)
-               {
-                 MDEBUG_PRINT ("\n");
-                 return -1;
-               }
-           }
-         /* If MAP is still not the root map, shift to the current
-            state.  */
-         if (ic_info->map != ic_info->state->map)
-           shift_state (ic, ic_info->state->name);
-       }
-    }
-  else
-    {
-      /* MAP can not handle KEY.  */
+    The minput_filter () function filters input key $KEY according to
+    input context $IC, and calls callback functions corresponding to
+    @b Minput_preedit_draw, @b Minput_status_draw, and
+    @b Minput_candidates_draw if the preedit text, the status, and the
+    current candidate are changed respectively.
 
-      /* If MAP is the root map of the initial state, it means that
-        the current input method can not handle KEY.  */
-      if (map == ((MIMState *) MPLIST_VAL (im_info->states))->map)
-       {
-         MDEBUG_PRINT (" unhandled\n");
-         return -1;
-       }
+    To make the input method commit the current preedit text (if any)
+    and shift to the initial state, call this function with #Mnil as
+    $KEY.
 
-      if (map != ic_info->state->map)
-       {
-         /* If MAP is not the root map... */
-         /* If MAP has branch actions, perform them.  */
-         if (map->branch_actions)
-           {
-             MDEBUG_PRINT (" branch-actions:");
-             if (take_action_list (ic, map->branch_actions) < 0)
-               {
-                 MDEBUG_PRINT ("\n");
-                 return -1;
-               }
-           }
-         /* If MAP is still not the root map, shift to the current
-            state. */
-         if (ic_info->map != ic_info->state->map)
-           shift_state (ic, ic_info->state->name);
-       }
-      else
-       {
-         /* MAP is the root map, perform branch actions (if any) or
-            shift to the initial state.  */
-         if (map->branch_actions)
-           {
-             MDEBUG_PRINT (" branch-actions:");
-             if (take_action_list (ic, map->branch_actions) < 0)
-               {
-                 MDEBUG_PRINT ("\n");
-                 return -1;
-               }
-           }
-         else
-           shift_state (ic, Mnil);
-       }
-    }
-  MDEBUG_PRINT ("\n");
-  return 0;
-}
+    To inform the input method about the focus-out event, call this
+    function with @b Minput_focus_out as $KEY.
 
-static void
-reset_ic (MInputContext *ic, MSymbol ignore)
-{
-  MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
-  MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
-  MText *status;
-  MPlist *plist;
+    To inform the input method about the focus-in event, call this
+    function with @b Minput_focus_in as $KEY.
 
-  MDEBUG_PRINT ("\n  [IM] reset\n");
+    To inform the input method about the focus-move event (i.e. input
+    spot change within the same input context), call this function
+    with @b Minput_focus_move as $KEY.
 
-  ic_info->state = (MIMState *) MPLIST_VAL (im_info->states);
-  ic_info->prev_state = NULL;
-  ic_info->map = ic_info->state->map;
-  ic_info->state_key_head = ic_info->key_head = 0;
-  MLIST_RESET (ic_info);
-  ic_info->key_unhandled = 0;
+    @return
+    If $KEY is filtered out, this function returns 1.  In that case,
+    the caller should discard the key.  Otherwise, it returns 0, and
+    the caller should handle the key, for instance, by calling the
+    function minput_lookup () with the same key.  */
 
-  if (mtext_nchars (ic->produced) > 0)
-    mtext_reset (ic->produced);
-  if (mtext_nchars (ic->preedit) > 0)
-    {
-      mtext_reset (ic->preedit);
-      ic->preedit_changed = ic->cursor_pos_changed = 1;
-    }
-  MPLIST_DO (plist, ic_info->markers)
-    MPLIST_VAL (plist) = 0;
+/***ja
+    @brief ÆþÎÏ¥­¡¼¤ò¥Õ¥£¥ë¥¿¤¹¤ë.
 
-  M17N_OBJECT_UNREF (ic_info->vars);
-  ic_info->vars = mplist ();
-  plist = get_nested_list (ic->im->language, ic->im->name, Mnil, M_variable);
-  MPLIST_DO (plist, plist)
-    {
-      MSymbol var = MPLIST_SYMBOL (plist);
-      MPlist *pl;
+    ´Ø¿ô minput_filter () ¤ÏÆþÎÏ¥­¡¼ $KEY ¤òÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC 
+    ¤Ë±þ¤¸¤Æ¥Õ¥£¥ë¥¿¤·¡¢preedit ¥Æ¥­¥¹¥È¡¢¥¹¥Æ¡¼¥¿¥¹¡¢¸½»þÅÀ¤Ç¤Î¸õÊ䤬ÊѲ½¤·¤¿»þÅÀ¤Ç¡¢¤½¤ì¤¾¤ì
+    @b Minput_preedit_draw, @b Minput_status_draw,
+    @b Minput_candidates_draw ¤ËÂбþ¤¹¤ë¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤ò¸Æ¤Ö¡£
 
-      plist = MPLIST_NEXT (plist);
-      pl = MPLIST_PLIST (plist);
-      pl = MPLIST_NEXT (pl);   /* Skip description.  */
-      mplist_push (ic_info->vars, MPLIST_KEY (pl), MPLIST_VAL (pl));
-      mplist_push (ic_info->vars, Msymbol, var);
-    }
+    @return 
+    $KEY ¤¬¥Õ¥£¥ë¥¿¤µ¤ì¤ì¤Ð¡¢¤³¤Î´Ø¿ô¤Ï 1 ¤òÊÖ¤¹¡£
+    ¤³¤Î¾ì¹ç¸Æ¤Ó½Ð¤·Â¦¤Ï¤³¤Î¥­¡¼¤ò¼Î¤Æ¤ë¤Ù¤­¤Ç¤¢¤ë¡£
+    ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð 0 ¤òÊÖ¤·¡¢¸Æ¤Ó½Ð¤·Â¦¤Ï¡¢¤¿¤È¤¨¤ÐƱ¤¸¥­¡¼¤Ç´Ø¿ô minput_lookup ()
+    ¤ò¸Æ¤Ö¤Ê¤É¤·¤Æ¡¢¤³¤Î¥­¡¼¤ò½èÍý¤¹¤ë¡£
+
+    @latexonly \IPAlabel{minput_filter} @endlatexonly
+*/
+
+int
+minput_filter (MInputContext *ic, MSymbol key, void *arg)
+{
+  int ret;
 
-  if (ic->candidate_list)
-    {
-      M17N_OBJECT_UNREF (ic->candidate_list);
-      ic->candidate_list = NULL;
-      ic->candidates_changed |= MINPUT_CANDIDATES_LIST_CHANGED;
-    }
-  if (ic->candidate_show)
-    {
-      ic->candidate_show = 0;
-      ic->candidates_changed |= MINPUT_CANDIDATES_SHOW_CHANGED;
-    }
-  if (ic->candidate_index > 0)
-    {
-      ic->candidate_index = 0;
-      ic->candidates_changed |= MINPUT_CANDIDATES_INDEX_CHANGED;
-    }
+  if (! ic
+      || ! ic->active)
+    return 0;
+  if (ic->im->driver.callback_list
+      && mtext_nchars (ic->preedit) > 0)
+    minput_callback (ic, Minput_preedit_draw);
 
-  mtext_reset (ic_info->preedit_saved);
-  ic_info->state_pos = ic->cursor_pos = 0;
+  ret = (*ic->im->driver.filter) (ic, key, arg);
 
-  status = ic_info->state->title ? ic_info->state->title : im_info->title;
-  if (ic->status != status)
+  if (ic->im->driver.callback_list)
     {
-      ic->status = status;
-      ic->status_changed = 1;
+      if (ic->preedit_changed)
+       minput_callback (ic, Minput_preedit_draw);
+      if (ic->status_changed)
+       minput_callback (ic, Minput_status_draw);
+      if (ic->candidates_changed)
+       minput_callback (ic, Minput_candidates_draw);
     }
+
+  return ret;
 }
 
-static int
-open_im (MInputMethod *im)
-{
-  MInputMethodInfo *im_info = get_im_info (im->language, im->name, Mnil);
+/*=*/
 
-  if (! im_info)
-    MERROR (MERROR_IM, -1);
-  im->info = im_info;
-  im_info->im = im;
-  return 0;
-}
+/***en
+    @brief Look up a text produced in the input context.
 
-static void
-close_im (MInputMethod *im)
-{
-  im->info = NULL;
-}
+    The minput_lookup () function looks up a text in the input context
+    $IC.  $KEY must be identical to the one that was used in the previous call of
+    minput_filter ().
 
-static int
-create_ic (MInputContext *ic)
-{
-  MInputMethod *im = ic->im;
-  MInputMethodInfo *im_info = (MInputMethodInfo *) im->info;
-  MInputContextInfo *ic_info;
-  MPlist *plist;
+    If a text was produced by the input method, it is concatenated
+    to M-text $MT.
 
-  if (ic->info)
-    ic_info = (MInputContextInfo *) ic->info;
-  else
-    {
-      MSTRUCT_CALLOC (ic_info, MERROR_IM);
-      ic->info = ic_info;
-    }
-  MLIST_INIT1 (ic_info, keys, 8);
-  ic_info->markers = mplist ();
-  ic_info->vars = mplist ();
-  plist = get_nested_list (im->language, im->name, Mnil, M_variable);
-  MPLIST_DO (plist, plist)
-    {
-      MSymbol var = MPLIST_SYMBOL (plist);
-      MPlist *pl;
+    This function calls #MInputDriver::lookup .
 
-      plist = MPLIST_NEXT (plist);
-      pl = MPLIST_PLIST (plist);
-      pl = MPLIST_NEXT (pl);   /* Skip description.  */
-      mplist_push (ic_info->vars, MPLIST_KEY (pl), MPLIST_VAL (pl));
-      mplist_push (ic_info->vars, Msymbol, var);
-    }
+    @return
+    If $KEY was correctly handled by the input method, this function
+    returns 0.  Otherwise, it returns -1, even though some text
+    might be produced in $MT.  */
 
-  ic_info->preedit_saved = mtext ();
-  if (im_info->externals)
-    {
-      MPlist *func_args = mplist (), *plist;
+/***ja
+    @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥ÈÃæ¤Î¥Æ¥­¥¹¥È¤òõ¤¹.
 
-      mplist_add (func_args, Mt, ic);
-      MPLIST_DO (plist, im_info->externals)
-       {
-         MIMExternalModule *external = MPLIST_VAL (plist);
-         MIMExternalFunc func
-           = (MIMExternalFunc) mplist_get (external->func_list, Minit);
+    ´Ø¿ô minput_lookup () ¤ÏÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC Ãæ¤Î¥Æ¥­¥¹¥È¤òõ¤¹¡£
+    $KEY ¤Ï´Ø¿ô minput_filter () ¤Ø¤ÎľÁ°¤Î¸Æ¤Ó½Ð¤·¤ËÍѤ¤¤é¤ì¤¿¤â¤Î¤ÈƱ¤¸¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
 
-         if (func)
-           (func) (func_args);
-       }
-      M17N_OBJECT_UNREF (func_args);
-    }
-  reset_ic (ic, Mnil);
-  return 0;
-}
+    ¥Æ¥­¥¹¥È¤¬ÆþÎϥ᥽¥Ã¥É¤Ë¤è¤Ã¤ÆÀ¸À®¤µ¤ì¤Æ¤¤¤ì¤Ð¡¢¥Æ¥­¥¹¥È¤Ï M-text
+    $MT ¤ËÏ¢·ë¤µ¤ì¤ë¡£
 
-static void
-destroy_ic (MInputContext *ic)
-{
-  MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
-  MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
+    ¤³¤Î´Ø¿ô¤Ï¡¢#MInputDriver::lookup ¤ò¸Æ¤Ö¡£
 
-  if (im_info->externals)
-    {
-      MPlist *func_args = mplist (), *plist;
+    @return 
+    $KEY ¤¬ÆþÎϥ᥽¥Ã¥É¤Ë¤è¤Ã¤ÆŬÀڤ˽èÍý¤Ç¤­¤ì¤Ð¡¢¤³¤Î´Ø¿ô¤Ï 0 ¤òÊÖ¤¹¡£
+    ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£
+    ¤³¤Î¾ì¹ç¤Ç¤â $MT ¤Ë²¿¤é¤«¤Î¥Æ¥­¥¹¥È¤¬À¸À®¤µ¤ì¤Æ¤¤¤ë¤³¤È¤¬¤¢¤ë¡£
 
-      mplist_add (func_args, Mt, ic);
-      MPLIST_DO (plist, im_info->externals)
-       {
-         MIMExternalModule *external = MPLIST_VAL (plist);
-         MIMExternalFunc func
-           = (MIMExternalFunc) mplist_get (external->func_list, Mfini);
+    @latexonly \IPAlabel{minput_lookup} @endlatexonly  */
 
-         if (func)
-           (func) (func_args);
-       }
-      M17N_OBJECT_UNREF (func_args);
-    }
-  MLIST_FREE1 (ic_info, keys);
-  M17N_OBJECT_UNREF (ic_info->preedit_saved);
-  M17N_OBJECT_UNREF (ic_info->markers);
-  M17N_OBJECT_UNREF (ic_info->vars);
-  M17N_OBJECT_UNREF (ic_info->preceding_text);
-  M17N_OBJECT_UNREF (ic_info->following_text);
-  free (ic->info);
+int
+minput_lookup (MInputContext *ic, MSymbol key, void *arg, MText *mt)
+{
+  return (ic ? (*ic->im->driver.lookup) (ic, key, arg, mt) : -1);
 }
+/*=*/
 
+/***en
+    @brief Set the spot of the input context.
 
-/** Handle the input key KEY in the current state and map of IC->info.
-    If KEY is handled but no text is produced, return 0, otherwise
-    return 1.
+    The minput_set_spot () function sets the spot of input context $IC
+    to coordinate ($X, $Y ) with the height specified by $ASCENT and $DESCENT .
+    The semantics of these values depends on the input method driver.
 
-    Ignore ARG.  */
+    For instance, a driver designed to work in a CUI environment may
+    use $X and $Y as the column- and row numbers, and may ignore $ASCENT and
+    $DESCENT .  A driver designed to work in a window system may
+    interpret $X and $Y as the pixel offsets relative to the origin of the
+    client window, and may interpret $ASCENT and $DESCENT as the ascent- and
+    descent pixels of the line at ($X . $Y ).
 
-static int
-filter (MInputContext *ic, MSymbol key, void *arg)
-{
-  MInputMethodInfo *im_info = (MInputMethodInfo *) ic->im->info;
-  MInputContextInfo *ic_info = (MInputContextInfo *) ic->info;
-  int i = 0;
+    $FONTSIZE specifies the fontsize of preedit text in 1/10 point.
 
-  if (! ic_info->state)
-    {
-      ic_info->key_unhandled = 1;
-      return 0;
-    }
-  mtext_reset (ic->produced);
-  ic->status_changed = ic->preedit_changed = ic->candidates_changed = 0;
-  M17N_OBJECT_UNREF (ic_info->preceding_text);
-  M17N_OBJECT_UNREF (ic_info->following_text);
-  MLIST_APPEND1 (ic_info, keys, key, MERROR_IM);
-  ic_info->key_unhandled = 0;
+    $MT and $POS are the M-text and the character position at the spot.
+    $MT may be @c NULL, in which case, the input method cannot get
+    information about the text around the spot.  */
 
-  /* If KEY has Meta or Alt modifier, put M_key_alias property.  */
-  if (key != Mnil)
-    {
-      if (! msymbol_get (key, M_key_alias)
-         && (strchr (MSYMBOL_NAME (key), 'M')
-             || strchr (MSYMBOL_NAME (key), 'A')))
-       {
-         char *name = MSYMBOL_NAME (key);
-         char *meta_or_alt;
+/***ja
+    @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Î¥¹¥Ý¥Ã¥È¤òÀßÄꤹ¤ë.
 
-         while (name[0] && name[1] == '-'
-                && (name[0] != 'M' && name[0] != 'A'))
-           name += 2;
-         if ((name[0] == 'M' || name[0] == 'A') && name[1] == '-')
-           {
-             MSymbol alias;
-
-             meta_or_alt = name;
-             name = alloca (MSYMBOL_NAMELEN (key) + 1);
-             memcpy (name, MSYMBOL_NAME (key), MSYMBOL_NAMELEN (key) + 1);
-             name[meta_or_alt - MSYMBOL_NAME (key)]
-               = *meta_or_alt == 'M' ? 'A' : 'M';
-             alias = msymbol (name);
-             msymbol_put (key, M_key_alias, alias);
-           }
-       }
-      else if (MSYMBOL_NAMELEN (key) == 3
-              && MSYMBOL_NAME (key)[0] == 'S'
-              && MSYMBOL_NAME (key)[1] == '-'
-              && MSYMBOL_NAME (key)[2] >= 'A' && MSYMBOL_NAME (key)[2] <= 'Z')
-       msymbol_put (key, M_key_alias, one_char_symbol[(int)MSYMBOL_NAME (key)[2]]);
-    }
+    ´Ø¿ô minput_set_spot () ¤Ï¡¢ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤Î¥¹¥Ý¥Ã¥È¤ò¡¢ºÂɸ ($X, $Y )
+    ¤Î°ÌÃ֤ˠ¡¢¹â¤µ $ASCENT¡¢ $DESCENT 
+    ¤ÇÀßÄꤹ¤ë¡£ ¤³¤ì¤é¤ÎÃͤΰÕÌ£¤ÏÆþÎϥ᥽¥Ã¥É¥É¥é¥¤¥Ð¤Ë°Í¸¤¹¤ë¡£
 
-  do {
-    if (handle_key (ic) < 0)
-      {
-       /* KEY was not handled.  Delete it from the current key sequence.  */
-       if (ic_info->used > 0)
-         {
-           memmove (ic_info->keys, ic_info->keys + 1,
-                    sizeof (int) * (ic_info->used - 1));
-           ic_info->used--;
-         }
-       /* This forces returning 1.  */
-       ic_info->key_unhandled = 1;
-       break;
-      }
-    if (i++ == 100)
-      {
-       mdebug_hook ();
-       reset_ic (ic, Mnil);
-       ic_info->key_unhandled = 1;
-       break;
-      }
-    /* Break the loop if all keys were handled.  */
-  } while (ic_info->key_head < ic_info->used);
+    ¤¿¤È¤¨¤Ð CUI ´Ä¶­¤ÇÆ°ºî¤¹¤ë¥É¥é¥¤¥Ð¤Ï $X ¤È $Y 
+    ¤ò¤½¤ì¤¾¤ìÎó¤È¹Ô¤ÎÈÖ¹æ¤È¤·¤ÆÍѤ¤¡¢$ASCENT ¤È $DESCENT 
+    ¤ò̵»ë¤¹¤ë¤«¤â¤·¤ì¤Ê¤¤¡£ ¤Þ¤¿¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥àÍѤΥɥ饤¥Ð¤Ï
+    $X ¤È $Y ¤ò¥¯¥é¥¤¥¢¥ó¥È¥¦¥£¥ó¥É¥¦¤Î¸¶ÅÀ¤«¤é¤Î¥ª¥Õ¥»¥Ã¥È¤ò¥Ô¥¯¥»¥ëñ°Ì¤Çɽ¤·¤¿¤â¤Î¤È¤·¤Æ°·¤¤¡¢
+    $ASCENT ¤È $DESCENT ¤ò ($X . $Y )
+    ¤ÎÎó¤Î¥¢¥»¥ó¥È¤È¥Ç¥£¥»¥ó¥È¤ò¥Ô¥¯¥»¥ëñ°Ì¤Çɽ¤·¤¿¤â¤Î¤È¤·¤Æ°·¤¦¤«¤â¤·¤ì¤Ê¤¤¡£
 
-  /* If the current map is the root of the initial state, we should
-     produce any preedit text in ic->produced.  */
-  if (ic_info->map == ((MIMState *) MPLIST_VAL (im_info->states))->map
-      && mtext_nchars (ic->preedit) > 0)
-    shift_state (ic, ((MIMState *) MPLIST_VAL (im_info->states))->name);
+    $FONTSIZE ¤Ë¤Ï preedit ¥Æ¥­¥¹¥È¤Î¥Õ¥©¥ó¥È¥µ¥¤¥º¤ò 1/10 ¥Ý¥¤¥ó¥Èñ°Ì¤Ç»ØÄꤹ¤ë¡£
 
-  if (mtext_nchars (ic->produced) > 0)
-    {
-      MSymbol lang = msymbol_get (ic->im->language, Mlanguage);
+    $MT ¤È $POS ¤Ï¤½¤Î¥¹¥Ý¥Ã¥È¤Î M-text ¤Èʸ»ú°ÌÃ֤Ǥ¢¤ë¡£$MT ¤Ï @c
+    NULL ¤Ç¤â¤è¤¯¡¢¤½¤Î¾ì¹ç¤Ë¤ÏÆþÎϥ᥽¥Ã¥É¤Ï¥¹¥Ý¥Ã¥È¼þÊդΥƥ­¥¹¥È¤Ë´Ø¤¹¤ë¾ðÊó¤òÆÀ¤ë¤³¤È¤¬¤Ç¤­¤Ê¤¤¡£
+    */
 
-      if (lang != Mnil)
-       mtext_put_prop (ic->produced, 0, mtext_nchars (ic->produced),
-                       Mlanguage, ic->im->language);
-    }
+void
+minput_set_spot (MInputContext *ic, int x, int y,
+                int ascent, int descent, int fontsize,
+                MText *mt, int pos)
+{
+  ic->spot.x = x;
+  ic->spot.y = y;
+  ic->spot.ascent = ascent;
+  ic->spot.descent = descent;
+  ic->spot.fontsize = fontsize;
+  ic->spot.mt = mt;
+  ic->spot.pos = pos;
+  if (ic->im->driver.callback_list)
+    minput_callback (ic, Minput_set_spot);
+}
+/*=*/
 
-  return (! ic_info->key_unhandled && mtext_nchars (ic->produced) == 0);
+/***en
+    @brief Toggle input method.
+
+    The minput_toggle () function toggles the input method associated
+    with input context $IC.  */
+/***ja
+    @brief ÆþÎϥ᥽¥Ã¥É¤òÀÚÂؤ¨¤ë.
+
+    ´Ø¿ô minput_toggle () ¤ÏÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC 
+    ¤ËÂбþÉÕ¤±¤é¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤ò¥È¥°¥ë¤¹¤ë¡£
+    */
+
+void
+minput_toggle (MInputContext *ic)
+{
+  if (ic->im->driver.callback_list)
+    minput_callback (ic, Minput_toggle);
+  ic->active = ! ic->active;
 }
 
+/*=*/
+
+/***en
+    @brief Reset an input context.
 
-/** Return 1 if the last event or key was not handled, otherwise
-    return 0.
+    The minput_reset_ic () function resets input context $IC by
+    calling a callback function corresponding to @b Minput_reset.  It
+    resets the status of $IC to its initial one.  As the
+    current preedit text is deleted without commitment, if necessary,
+    call minput_filter () with the arg @b key #Mnil to force the input
+    method to commit the preedit in advance.  */
 
-    There is no need of looking up because ic->produced should already
-    contain the produced text (if any).
+/***ja
+    @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤ò¥ê¥»¥Ã¥È¤¹¤ë.
 
-    Ignore KEY.  */
+    ´Ø¿ô minput_reset_ic () ¤Ï @b Minput_reset ¤ËÂбþ¤¹¤ë¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô
+    ¤ò¸Æ¤Ö¤³¤È¤Ë¤è¤Ã¤ÆÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤ò¥ê¥»¥Ã¥È¤¹¤ë¡£¥ê¥»¥Ã¥È¤È¤Ï¡¢
+    ¼ÂºÝ¤Ë¤ÏÆþÎϥ᥽¥Ã¥É¤ò½é´ü¾õÂ֤˰ܤ¹¤³¤È¤Ç¤¢¤ë¡£¸½ºßÆþÎÏÃæ¤Î¥Æ¥­¥¹
+    ¥È¤Ï¥³¥ß¥Ã¥È¤µ¤ì¤ë¤³¤È¤Ê¤¯ºï½ü¤µ¤ì¤ë¤Î¤Ç¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é
+    ¥à¤Ï¡¢É¬Íפʤé¤Ðͽ¤á minput_filter () ¤ò°ú¿ô @b key #Mnil ¤Ç¸Æ¤ó¤Ç
+    ¶¯À©Åª¤Ë¥×¥ê¥¨¥Ç¥£¥Ã¥È¥Æ¥­¥¹¥È¤ò¥³¥ß¥Ã¥È¤µ¤»¤ë¤³¤È¡£  */
 
-static int
-lookup (MInputContext *ic, MSymbol key, void *arg, MText *mt)
+void
+minput_reset_ic (MInputContext *ic)
 {
-  mtext_cat (mt, ic->produced);
-  mtext_reset (ic->produced);
-  return (((MInputContextInfo *) ic->info)->key_unhandled ? -1 : 0);
+  if (ic->im->driver.callback_list)
+    minput_callback (ic, Minput_reset);
 }
 
-static MPlist *load_im_info_keys;
+/*=*/
 
-static MPlist *
-load_partial_im_info (MSymbol language, MSymbol name,
-                     MSymbol extra, MSymbol key)
-{
-  MDatabase *mdb;
-  MPlist *plist;
+/***en
+    @brief Get title and icon filename of an input method.
+
+    The minput_get_title_icon () function returns a plist containing a
+    title and icon filename (if any) of an input method specified by
+    $LANGUAGE and $NAME.
 
-  if (language == Mnil)
-    MERROR (MERROR_IM, NULL);
-  mdb = mdatabase_find (Minput_method, language, name, extra);
-  if (! mdb)
-    MERROR (MERROR_IM, NULL);
+    The first element of the plist has key #Mtext and the value is an
+    M-text of the title for identifying the input method.  The second
+    element (if any) has key #Mtext and the value is an M-text of the
+    icon image (absolute) filename for the same purpose.
 
-  mplist_push (load_im_info_keys, key, Mt);
-  plist = mdatabase__load_for_keys (mdb, load_im_info_keys);
-  mplist_pop (load_im_info_keys);
-  return plist;
-}
+    @return
+    If there exists a specified input method and it defines an title,
+    a plist is returned.  Otherwise, NULL is returned.  The caller
+    must free the plist by m17n_object_unref ().  */
+/***ja
+    @brief ÆþÎϥ᥽¥Ã¥É¤Î¥¿¥¤¥È¥ë¤È¥¢¥¤¥³¥óÍÑ¥Õ¥¡¥¤¥ë̾¤òÆÀ¤ë.
 
+    ´Ø¿ô minput_get_title_icon () ¤Ï¡¢ $LANGUAGE ¤È $NAME ¤Ç»ØÄꤵ¤ì¤ë
+    ÆþÎϥ᥽¥Ã¥É¤Î¥¿¥¤¥È¥ë¤È¡Ê¤¢¤ì¤Ð¡Ë¥¢¥¤¥³¥óÍÑ¥Õ¥¡¥¤¥ë¤ò´Þ¤à plist ¤ò
+    ÊÖ¤¹¡£
 
-static MInputMethodInfo *
-get_im_info (MSymbol language, MSymbol name, MSymbol extra)
+    plist ¤ÎÂè°ìÍ×ÁǤϡ¢#Mtext ¤ò¥­¡¼¤Ë»ý¤Á¡¢ÃͤÏÆþÎϥ᥽¥Ã¥É¤ò¼±Ê̤¹¤ë
+    ¥¿¥¤¥È¥ë¤òɽ¤¹ M-text ¤Ç¤¢¤ë¡£ÂèÆóÍ×ÁǤ¬¤¢¤ì¤Ð¡¢¥­¡¼¤Ï #Mtext ¤Ç¤¢
+    ¤ê¡¢Ãͤϼ±ÊÌÍÑ¥¢¥¤¥³¥ó²èÁü¥Õ¥¡¥¤¥ë¤ÎÀäÂХѥ¹¤òɽ¤¹ M-text ¤Ç¤¢¤ë¡£
+
+    @return
+    »ØÄê¤ÎÆþÎϥ᥽¥Ã¥É¤¬Â¸ºß¤·¡¢¥¿¥¤¥È¥ë¤¬ÄêµÁ¤µ¤ì¤Æ¤¤¤ì¤Ð
+     plist ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð NULL ¤òÊÖ¤¹¡£¸Æ½Ð¦¤Ï
+     ´Ø¿ô m17n_object_unref () ¤òÍѤ¤¤Æ plist ¤ò²òÊü¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£  */
+
+MPlist *
+minput_get_title_icon (MSymbol language, MSymbol name)
 {
-  MDatabase *mdb;
+  MInputMethodInfo *im_info;
   MPlist *plist;
-  MInputMethodInfo *im_info = NULL;
+  char *file = NULL;
+  MText *mt;
 
-  if (language == Mnil)
-    MERROR (MERROR_IM, NULL);
-  mdb = mdatabase_find (Minput_method, language, name, extra);
-  if (! mdb)
-    MERROR (MERROR_IM, NULL);
+  MINPUT__INIT ();
 
-  if (! im_info_list)
-    im_info_list = mplist ();
-  else if ((plist = mplist_find_by_value (im_info_list, mdb)))
+  im_info = get_im_info (language, name, Mnil, Mtitle);
+  if (! im_info || !im_info->title)
+    return NULL;
+  mt = mtext_get_prop (im_info->title, 0, Mtext);
+  if (mt)
+    file = mdatabase__find_file ((char *) MTEXT_DATA (mt));
+  else
     {
-      if (mdatabase__check (mdb))
+      char *buf = alloca (MSYMBOL_NAMELEN (language) + MSYMBOL_NAMELEN (name)
+                         + 12);
+
+      sprintf (buf, "icons/%s-%s.png", (char *) MSYMBOL_NAME (language), 
+              (char *) MSYMBOL_NAME (name));
+      file = mdatabase__find_file (buf);
+      if (! file && language == Mt)
        {
-         plist = MPLIST_NEXT (plist);
-         im_info = MPLIST_VAL (plist);
-         return im_info;
+         sprintf (buf, "icons/%s.png", (char *) MSYMBOL_NAME (name));
+         file = mdatabase__find_file (buf);
        }
-      mplist_pop (plist);
-      free_im_info (MPLIST_VAL (plist));
-      mplist_pop (plist);
     }
 
-  plist = mdatabase_load (mdb);
-  if (! plist)
-    MERROR (MERROR_IM, NULL);
-  im_info = load_im_info (language, name, plist);
-  M17N_OBJECT_UNREF (plist);
-  if (! im_info)
-    MERROR (MERROR_IM, NULL);
-  mplist_push (im_info_list, Mt, im_info);
-  mplist_push (im_info_list, Mt, mdb);
-  return im_info;
+  plist = mplist ();
+  mplist_add (plist, Mtext, im_info->title);
+  if (file)
+    {
+      mt = mtext__from_data (file, strlen (file), MTEXT_FORMAT_UTF_8, 1);
+      free (file);
+      mplist_add (plist, Mtext, mt);
+      M17N_OBJECT_UNREF (mt);
+    }
+  return plist;
 }
 
-\f
-/* Input method command handler.  */
+/*=*/
 
-/* List of all (global and local) commands. 
-   (LANG:(IM-NAME:(COMMAND ...) ...) ...) ...
-   COMMAND is CMD-NAME:(mtext:DESCRIPTION plist:KEYSEQ ...))
-   Global commands are stored as (t (t COMMAND ...))  */
+/***en
+    @brief Get description text of an input method.
 
-/* Check if PLIST is a valid command key sequence.
-   PLIST must be NULL or:
-   [ symbol:KEY | integer:KEY ] ...  */
+    The minput_get_description () function returns an M-text that
+    describes the input method specified by $LANGUAGE and $NAME.
 
-static int
-check_command_keyseq (MPlist *plist)
-{
-  if (! plist)
-    return 0;
-  MPLIST_DO (plist, plist)
-    {
-      if (MPLIST_SYMBOL_P (plist))
-       continue;
-      else if (MPLIST_INTEGER_P (plist))
-       {
-         int n = MPLIST_INTEGER (plist);
+    @return
+    If the specified input method has a description text, a pointer to
+    #MText is returned.  The caller has to free it by m17n_object_unref ().
+    If the input method does not have a description text, @c NULL is
+    returned.  */
+/***ja
+    @brief ÆþÎϥ᥽¥Ã¥É¤ÎÀâÌÀ¥Æ¥­¥¹¥È¤òÆÀ¤ë.
 
-         if (n < 0 || n > 9)
-           return -1;
-         MPLIST_KEY (plist) = Msymbol;
-         MPLIST_VAL (plist) = one_char_symbol['0' + 9];
-       }
-      else
-       return -1;
-    }
-  return 0;
-}
+    ´Ø¿ô minput_get_description () ¤Ï¡¢$LANGUAGE ¤È $NAME ¤Ë¤è¤Ã¤Æ»ØÄê
+    ¤µ¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤òÀâÌÀ¤¹¤ë M-text ¤òÊÖ¤¹¡£
 
-/* Check if PLIST has this form:
-     ([ plist:([ symbol:KEY | integer:KEY ]) | mtext:KEYSEQ ]
-      ...)
-   If the form of PLIST matches, return 0, otherwise return -1.  */
+    @return
+    »ØÄꤵ¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤¬ÀâÌÀ¤¹¤ë¥Æ¥­¥¹¥È¤ò»ý¤Ã¤Æ¤¤¤ì¤Ð¡¢
+    #MText ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¸Æ¤Ó½Ð¤·Â¦¤Ï¡¢¤½¤ì¤ò m17n_object_unref
+    () ¤òÍѤ¤¤Æ²òÊü¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ÆþÎϥ᥽¥Ã¥É¤ËÀâÌÀ¥Æ¥­¥¹¥È¤¬Ìµ¤±
+    ¤ì¤Ð@c NULL ¤òÊÖ¤¹¡£ */
 
-static int
-check_command_list (MPlist *plist)
+MText *
+minput_get_description (MSymbol language, MSymbol name)
 {
-  MPLIST_DO (plist, plist)
-    {
-      if (MPLIST_PLIST_P (plist))
-       {
-         MPlist *pl = MPLIST_PLIST (plist);
+  MInputMethodInfo *im_info;
+  MSymbol extra;
 
-         MPLIST_DO (pl, pl)
-           if (! MPLIST_SYMBOL_P (pl) && ! MPLIST_INTEGER_P (pl))
-             return -1;
-       }
-      else if (! MPLIST_MTEXT_P (plist))
-       return -1;
-    }
-  return 0;
-}
+  MINPUT__INIT ();
 
+  if (name != Mnil)
+    extra = Mnil;
+  else
+    extra = language, language = Mt;
 
-\f
-/* Input method variable handler.  */
+  im_info = get_im_info (language, name, extra, Mdescription);
+  if (! im_info || ! im_info->description)
+    return NULL;
+  M17N_OBJECT_REF (im_info->description);
+  return im_info->description;
+}
 
-/* Check if PLIST has this form:
-     (TYPE:VAL   ;; TYPE ::= integer | mtext | symbol
-      VALID-VALUE
-      ...)
-   If the form of PLIST matches, return 0, otherwise return -1.  */
+/*=*/
 
-static int
-check_variable_list (MPlist *plist)
-{
-  MSymbol type = MPLIST_KEY (plist);
-  MPlist *p; 
+/***en
+    @brief Get information about input method command(s).
 
-  if (type != Minteger && type != Mtext && type != Msymbol)
-    return -1;
-  MPLIST_DO (plist, MPLIST_NEXT (plist))
-    {
-      if (type == Minteger && MPLIST_PLIST_P (plist))
-       {
-         MPLIST_DO (p, MPLIST_PLIST (plist))
-           if (! MPLIST_INTEGER_P (p))
-             return -1;
-       }
-      else if (type != MPLIST_KEY (plist))
-       return -1;
-    }
-  return 0;
-}
+    The minput_get_command () function returns information about
+    the command $COMMAND of the input method specified by $LANGUAGE and
+    $NAME.  An input method command is a pseudo key event to which one
+    or more actual input key sequences are assigned.
 
-/* Support functions for mdebug_dump_im.  */
+    There are two kinds of commands, global and local.  A global
+    command has a global definition, and the description and the key
+    assignment may be inherited by a local command.  Each input method
+    defines a local command which has a local key assignment.  It may
+    also declare a local command that inherits the definition of a
+    global command of the same name.
 
-static void
-dump_im_map (MPlist *map_list, int indent)
-{
-  char *prefix;
-  MSymbol key = MPLIST_KEY (map_list);
-  MIMMap *map = (MIMMap *) MPLIST_VAL (map_list);
+    If $LANGUAGE is #Mt and $NAME is #Mnil, this function returns
+    information about a global command.  Otherwise information about a
+    local command is returned.
 
-  prefix = (char *) alloca (indent + 1);
-  memset (prefix, 32, indent);
-  prefix[indent] = '\0';
+    If $COMMAND is #Mnil, information about all commands is returned.
 
-  fprintf (stderr, "(\"%s\" ", msymbol_name (key));
-  if (map->map_actions)
-    mdebug_dump_plist (map->map_actions, indent + 2);
-  if (map->submaps)
-    {
-      MPLIST_DO (map_list, map->submaps)
-       {
-         fprintf (stderr, "\n%s  ", prefix);
-         dump_im_map (map_list, indent + 2);
-       }
-    }
-  if (map->branch_actions)
-    {
-      fprintf (stderr, "\n%s  (branch\n%s    ", prefix, prefix);
-      mdebug_dump_plist (map->branch_actions, indent + 4);
-      fprintf (stderr, ")");      
-    }
-  fprintf (stderr, ")");
-}
+    The return value is a @e well-formed plist (@ref m17nPlist) of this
+    format:
+@verbatim
+  ((NAME DESCRIPTION STATUS [KEYSEQ ...]) ...)
+@endverbatim
+    @c NAME is a symbol representing the command name.
 
+    @c DESCRIPTION is an M-text describing the command, or #Mnil if the
+    command has no description.
 
-static void
-dump_im_state (MIMState *state, int indent)
-{
-  char *prefix;
-  MPlist *map_list;
+    @c STATUS is a symbol representing how the key assignment is decided.
+    The value is #Mnil (the default key assignment), @b Mcustomized (the
+    key assignment is customized by per-user customization file), or
+    @b Mconfigured (the key assignment is set by the call of
+    minput_config_command ()).  For a local command only, it may also
+    be @b Minherited (the key assignment is inherited from the
+    corresponding global command).
 
-  prefix = (char *) alloca (indent + 1);
-  memset (prefix, 32, indent);
-  prefix[indent] = '\0';
+    @c KEYSEQ is a plist of one or more symbols representing a key
+    sequence assigned to the command.  If there's no KEYSEQ, the
+    command is currently disabled (i.e. no key sequence can trigger
+    actions of the command).
 
-  fprintf (stderr, "(%s", msymbol_name (state->name));
-  if (state->map->submaps)
-    {
-      MPLIST_DO (map_list, state->map->submaps)
-       {
-         fprintf (stderr, "\n%s  ", prefix);
-         dump_im_map (map_list, indent + 2);
-       }
-    }
-  fprintf (stderr, ")");
-}
+    If $COMMAND is not #Mnil, the first element of the returned plist
+    contains the information about $COMMAND.
 
-\f
+    @return
 
-int
-minput__init ()
-{
-  char *key_names[32]
-    = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-       "BackSpace", "Tab", "Linefeed", "Clear", NULL, "Return", NULL, NULL,
-       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-       NULL, NULL, NULL, "Escape", NULL, NULL, NULL, NULL };
-  char buf[6], buf2[256];
-  int i;
+    If the requested information was found, a pointer to a non-empty
+    plist is returned.  As the plist is kept in the library, the
+    caller must not modify nor free it.
 
-  Minput_method = msymbol ("input-method");
-  Minput_driver = msymbol ("input-driver");
-  Mtitle = msymbol ("title");
-  Mmacro = msymbol ("macro");
-  Mmodule = msymbol ("module");
-  Mmap = msymbol ("map");
-  Mstate = msymbol ("state");
-  Minclude = msymbol ("include");
-  Minsert = msymbol ("insert");
-  M_candidates = msymbol ("  candidates");
-  Mdelete = msymbol ("delete");
-  Mmove = msymbol ("move");
-  Mmark = msymbol ("mark");
-  Mpushback = msymbol ("pushback");
-  Mundo = msymbol ("undo");
-  Mcall = msymbol ("call");
-  Mshift = msymbol ("shift");
-  Mselect = msymbol ("select");
-  Mshow = msymbol ("show");
-  Mhide = msymbol ("hide");
-  Mcommit = msymbol ("commit");
-  Munhandle = msymbol ("unhandle");
-  Mset = msymbol ("set");
-  Madd = msymbol ("add");
-  Msub = msymbol ("sub");
-  Mmul = msymbol ("mul");
-  Mdiv = msymbol ("div");
-  Mequal = msymbol ("=");
-  Mless = msymbol ("<");
-  Mgreater = msymbol (">");
-  Mless_equal = msymbol ("<=");
-  Mgreater_equal = msymbol (">=");
-  Mcond = msymbol ("cond");
-  Mplus = msymbol ("+");
-  Mminus = msymbol ("-");
-  Mstar = msymbol ("*");
-  Mslush = msymbol ("/");
-  Mand = msymbol ("&");
-  Mor = msymbol ("|");
-  Mnot = msymbol ("!");
+    Otherwise (the specified input method or the specified command
+    does not exist), @c NULL is returned.  */
+/***ja
+    @brief ÆþÎϥ᥽¥Ã¥É¤Î¥³¥Þ¥ó¥É¤Ë´Ø¤¹¤ë¾ðÊó¤òÆÀ¤ë.
 
-  Mcandidates_group_size = msymbol ("candidates-group-size");
-  Mcandidates_charset = msymbol ("candidates-charset");
+    ´Ø¿ô minput_get_command () ¤Ï¡¢$LANGUAGE ¤È $NAME ¤Ç»ØÄꤵ¤ì¤ëÆþÎÏ
+    ¥á¥½¥Ã¥É¤Î¥³¥Þ¥ó¥É $COMMAND ¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÖ¤¹¡£ÆþÎϥ᥽¥Ã¥É¤Î¥³¥Þ
+    ¥ó¥É¤È¤Ï¡¢µ¿»÷¥­¡¼¥¤¥Ù¥ó¥È¤Ç¤¢¤ê¡¢£±¤Ä°Ê¾å¤Î¼ÂºÝ¤ÎÆþÎÏ¥­¡¼¥·¡¼¥¯¥¨
+    ¥ó¥¹¤¬³ä¤êÅö¤Æ¤é¤ì¤ë¡£
 
-  Minput_preedit_start = msymbol ("input-preedit-start");
-  Minput_preedit_done = msymbol ("input-preedit-done");
-  Minput_preedit_draw = msymbol ("input-preedit-draw");
-  Minput_status_start = msymbol ("input-status-start");
-  Minput_status_done = msymbol ("input-status-done");
-  Minput_status_draw = msymbol ("input-status-draw");
-  Minput_candidates_start = msymbol ("input-candidates-start");
-  Minput_candidates_done = msymbol ("input-candidates-done");
-  Minput_candidates_draw = msymbol ("input-candidates-draw");
-  Minput_set_spot = msymbol ("input-set-spot");
-  Minput_focus_move = msymbol ("input-focus-move");
-  Minput_focus_in = msymbol ("input-focus-in");
-  Minput_focus_out = msymbol ("input-focus-out");
-  Minput_toggle = msymbol ("input-toggle");
-  Minput_reset = msymbol ("input-reset");
-  Minput_get_surrounding_text = msymbol ("input-get-surrounding-text");
-  Minput_delete_surrounding_text = msymbol ("input-delete-surrounding-text");
+    ¥³¥Þ¥ó¥É¤Ë¤Ï¡¢¥°¥í¡¼¥Ð¥ë¤È¥í¡¼¥«¥ë¤Î£²¼ïÎब¤¢¤ë¡£¥°¥í¡¼¥Ð¥ë¤Ê¥³¥Þ¥ó¥É
+    ¤Ï¥°¥í¡¼¥Ð¥ë¤ËÄêµÁ¤µ¤ì¡¢¥í¡¼¥«¥ë¤Ê¥³¥Þ¥ó¥É¤Ï¤½¤ÎÀâÌÀ¤È¥­¡¼³ä¤êÅö¤Æ
+    ¤ò·Ñ¾µ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£³ÆÆþÎϥ᥽¥Ã¥É¤Ï¥í¡¼¥«¥ë¤Ê¥­¡¼³äÅö¤ò»ý¤Ä¥í¡¼
+    ¥«¥ë¤Ê¥³¥Þ¥ó¥É¤òÄêµÁ¤¹¤ë¡£¤Þ¤¿Æ±Ì¾¤Î¥°¥í¡¼¥Ð¥ë¤Ê¥³¥Þ¥ó¥É¤ÎÄêµÁ¤ò·Ñ
+    ¾µ¤¹¤ë¥í¡¼¥«¥ë¤Ê¥³¥Þ¥ó¥É¤òÀë¸À¤¹¤ë¤³¤È¤â¤Ç¤­¤ë¡£
 
-  Mcandidate_list = msymbol_as_managing_key ("  candidate-list");
-  Mcandidate_index = msymbol ("  candidate-index");
+    $LANGUAGE ¤¬ #Mt ¤Ç $NAME ¤¬ #Mnil ¤Î¾ì¹ç¤Ï¡¢¤³¤Î´Ø¿ô¤Ï¥°¥í¡¼¥Ð¥ë¥³
+    ¥Þ¥ó¥É¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¥í¡¼¥«¥ë¥³¥Þ¥ó¥É¤Ë´Ø¤¹¤ë¤â
+    ¤Î¤òÊÖ¤¹¡£
 
-  Minit = msymbol ("init");
-  Mfini = msymbol ("fini");
+    $COMMAND ¤¬ #Mnil ¤Î¾ì¹ç¤Ï¡¢¤¹¤Ù¤Æ¤Î¥³¥Þ¥ó¥É¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÖ¤¹¡£
 
-  M_key_alias = msymbol ("  key-alias");
-  M_description = msymbol ("description");
-  M_command = msymbol ("command");
-  M_variable = msymbol ("variable");
+    Ìá¤êÃͤϰʲ¼¤Î·Á¼°¤Î @e well-formed plist (@ref m17nPlist) ¤Ç¤¢¤ë¡£
 
-  load_im_info_keys = mplist ();
-  mplist_add (load_im_info_keys, Mstate, Mnil);
-  mplist_push (load_im_info_keys, Mmap, Mnil);
+@verbatim
+  ((NAME DESCRIPTION STATUS [KEYSEQ ...]) ...)
+@endverbatim
+    @c NAME ¤Ï¥³¥Þ¥ó¥É̾¤ò¼¨¤¹¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£
 
-  buf[0] = 'C';
-  buf[1] = '-';
-  buf[3] = '\0';
-  for (i = 0, buf[2] = '@'; i < ' '; i++, buf[2]++)
-    {
-      MSymbol alias;
+    @c DESCRIPTION ¤Ï¥³¥Þ¥ó¥É¤òÀâÌÀ¤¹¤ë M-text ¤Ç¤¢¤ë¤«¡¢ÀâÌÀ¤¬Ìµ¤¤¾ì¹ç¤Ë
+    ¤Ï #Mnil ¤Ç¤¢¤ë¡£
 
-      one_char_symbol[i] = msymbol (buf);
-      if (key_names[i])
-       {
-         alias = msymbol (key_names[i]);
-         msymbol_put (one_char_symbol[i], M_key_alias,  alias);
-       }
-      else
-       alias = one_char_symbol[i];
-      buf[2] += (i == 0) ? -32 : 32;
-      msymbol_put (alias, M_key_alias,  msymbol (buf));
-      buf[2] -= (i == 0) ? -32 : 32;
-    }
-  for (buf[2] = i; i < 127; i++, buf[2]++)
-    one_char_symbol[i] = msymbol (buf + 2);
-  one_char_symbol[i++] = msymbol ("Delete");
-  buf[2] = 'M';
-  buf[3] = '-';
-  buf[5] = '\0';
-  buf2[0] = 'M';
-  buf2[1] = '-';
-  for (buf[4] = '@'; i < 160; i++, buf[4]++)
-    {
-      one_char_symbol[i] = msymbol (buf);
-      if (key_names[i - 128])
-       {
-         strcpy (buf2 + 2, key_names[i - 128]);
-         msymbol_put (one_char_symbol[i], M_key_alias,  msymbol (buf2));
-       }
-    }
-  for (buf[4] = i - 128; i < 255; i++, buf[4]++)
-    one_char_symbol[i] = msymbol (buf + 2);
-  one_char_symbol[i] = msymbol ("M-Delete");
+    @c STATUS ¤Ï¥­¡¼³ä¤êÅö¤Æ¤¬¤É¤Î¤è¤¦¤ËÄê¤á¤é¤ì¤ë¤«¤ò¤¢¤é¤ï¤¹¥·¥ó¥Ü¥ë
+    ¤Ç¤¢¤ê¡¢¤½¤ÎÃͤϠ#Mnil ¡Ê¥Ç¥Õ¥©¥ë¥È¤Î³ä¤êÅö¤Æ¡Ë, @b Mcustomized ¡Ê¥æ¡¼
+    ¥¶Ëè¤Î¥«¥¹¥¿¥Þ¥¤¥º¥Õ¥¡¥¤¥ë¤Ë¤è¤Ã¤Æ¥«¥¹¥¿¥Þ¥¤¥º¤µ¤ì¤¿³ä¤êÅö¤Æ¡Ë,
+    @b Mconfigured ¡Êminput_config_command ()¤ò¸Æ¤Ö¤³¤È¤Ë¤è¤Ã¤ÆÀßÄꤵ¤ì¤ë
+    ³ä¤êÅö¤Æ¡Ë¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£¥í¡¼¥«¥ë¥³¥Þ¥ó¥É¤Î¾ì¹ç¤Ë¤Ï¡¢
+    @b Minherited ¡ÊÂбþ¤¹¤ë¥°¥í¡¼¥Ð¥ë¥³¥Þ¥ó¥É¤«¤é¤Î·Ñ¾µ¤Ë¤è¤ë³ä¤êÅö¤Æ¡Ë
+    ¤Ç¤â¤è¤¤¡£
 
-  command_list = variable_list = NULL;
+    @c KEYSEQ ¤Ï£±¤Ä°Ê¾å¤Î¥·¥ó¥Ü¥ë¤«¤é¤Ê¤ë plist ¤Ç¤¢¤ê¡¢³Æ¥·¥ó¥Ü¥ë¤Ï¥³¥Þ
+    ¥ó¥É¤Ë³ä¤êÅö¤Æ¤é¤ì¤Æ¤¤¤ë¥­¡¼¥·¡¼¥¯¥¨¥ó¥¹¤òɽ¤¹¡£KEYSEQ ¤¬Ìµ¤¤¾ì¹ç¤Ï¡¢
+    ¤½¤Î¥³¥Þ¥ó¥É¤Ï¸½¾õ¤Ç»ÈÍÑÉÔǽ¤Ç¤¢¤ë¡£¡Ê¤¹¤Ê¤ï¤Á¥³¥Þ¥ó¥É¤ÎÆ°ºî¤òµ¯
+    Æ°¤Ç¤­¤ë¥­¡¼¥·¡¼¥¯¥¨¥ó¥¹¤¬Ìµ¤¤¡£¡Ë
 
-  minput_default_driver.open_im = open_im;
-  minput_default_driver.close_im = close_im;
-  minput_default_driver.create_ic = create_ic;
-  minput_default_driver.destroy_ic = destroy_ic;
-  minput_default_driver.filter = filter;
-  minput_default_driver.lookup = lookup;
-  minput_default_driver.callback_list = mplist ();
-  mplist_put (minput_default_driver.callback_list, Minput_reset,
-             (void *) reset_ic);
-  minput_driver = &minput_default_driver;
-  return 0;
-}
+    $COMMAND ¤¬ #Mnil ¤Ç¤Ê¤±¤ì¤Ð¡¢ÊÖ¤µ¤ì¤ë plist ¤ÎºÇ½é¤ÎÍ×ÁǤϡ¢
+    $COMMAND ¤Ë´Ø¤¹¤ë¾ðÊó¤ò´Þ¤à¡£
 
-void
-minput__fini ()
-{
-  if (command_list)
-    {
-      M17N_OBJECT_UNREF (command_list);
-      command_list = NULL;
-    }
-  if (variable_list)
-    {
-      M17N_OBJECT_UNREF (variable_list);
-      variable_list = NULL;
-    }
+    @return
 
-  if (minput_default_driver.callback_list)
-    {
-      M17N_OBJECT_UNREF (minput_default_driver.callback_list);
-      minput_default_driver.callback_list = NULL;
-    }
-  if (minput_driver->callback_list)
-    {
-      M17N_OBJECT_UNREF (minput_driver->callback_list);
-      minput_driver->callback_list = NULL;
-    }
+    µá¤á¤é¤ì¤¿¾ðÊ󤬸«¤Ä¤«¤ì¤Ð¡¢¶õ¤Ç¤Ê¤¤ plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¥ê¥¹
+    ¥È¤Ï¥é¥¤¥Ö¥é¥ê¤¬´ÉÍý¤·¤Æ¤¤¤ë¤Î¤Ç¡¢¸Æ½Ð¦¤¬Êѹ¹¤·¤¿¤ê²òÊü¤·¤¿¤ê¤¹¤ë
+    ¤³¤È¤Ï¤Ç¤­¤Ê¤¤¡£
 
-  if (im_info_list)
-    {
-      while (! MPLIST_TAIL_P (im_info_list))
-       {
-         /* Pop (t . mdb) */
-         mplist_pop (im_info_list);
-         free_im_info ((MInputMethodInfo *) MPLIST_VAL (im_info_list));
-         /* Pop (t . im_info) */
-         mplist_pop (im_info_list);
-       }
-      M17N_OBJECT_UNREF (im_info_list);
-      im_info_list = NULL;
-    }
-  M17N_OBJECT_UNREF (load_im_info_keys);
-}
+    ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢¤¹¤Ê¤ï¤Á»ØÄê¤ÎÆþÎϥ᥽¥Ã¥É¤ä¥³¥Þ¥ó¥É¤¬Â¸ºß¤·¤Ê¤±¤ì¤Ð
+    @c NULL ¤òÊÖ¤¹¡£  */
 
-void
-minput__callback (MInputContext *ic, MSymbol command)
+#if EXAMPLE_CODE
+MText *
+get_im_command_description (MSymbol language, MSymbol name, MSymbol command)
 {
-  if (ic->im->driver.callback_list)
-    {
-      MInputCallbackFunc func
-       = (MInputCallbackFunc) mplist_get (ic->im->driver.callback_list,
-                                          command);
+  /* Return a description of the command COMMAND of the input method
+     specified by LANGUAGE and NAME.  */
+  MPlist *cmd = minput_get_command (langauge, name, command);
+  MPlist *plist;
 
-      if (func)
-       (func) (ic, command);
-    }
+  if (! cmds)
+    return NULL;
+  plist = mplist_value (cmds); /* (NAME DESCRIPTION STATUS KEY-SEQ ...) */
+  plist = mplist_next (plist); /* (DESCRIPTION STATUS KEY-SEQ ...) */
+  return  (mplist_key (plist) == Mtext
+          ? (MText *) mplist_value (plist)
+          : NULL);
 }
+#endif
 
-MSymbol
-minput__char_to_key (int c)
+MPlist *
+minput_get_command (MSymbol language, MSymbol name, MSymbol command)
 {
-  if (c < 0 || c >= 0x100)
-    return Mnil;
-
-  return one_char_symbol[c];
-}
+  MInputMethodInfo *im_info;
 
-/*** @} */
-#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
+  MINPUT__INIT ();
 
-\f
-/* External API */
+  im_info = get_im_info (language, name, Mnil, Mcommand);
+  if (! im_info
+      || ! im_info->configured_cmds
+      || MPLIST_TAIL_P (im_info->configured_cmds))
+    return NULL;
+  if (command == Mnil)
+    return im_info->configured_cmds;
+  return mplist__assq (im_info->configured_cmds, command);
+}
 
-/*** @addtogroup m17nInputMethod */
-/*** @{ */
 /*=*/
 
 /***en
-    @name Variables: Predefined symbols for callback commands.
+    @brief Configure the key sequence of an input method command.
 
-    These are the predefined symbols that are used as the @c COMMAND
-    argument of callback functions of an input method driver (see
-    #MInputDriver::callback_list ).  
+    The minput_config_command () function assigns a list of key
+    sequences $KEYSEQLIST to the command $COMMAND of the input method
+    specified by $LANGUAGE and $NAME.
 
-    Most of them don't require extra argument nor return any value;
-    exceptions are these:
+    If $KEYSEQLIST is a non-empty plist, it must be a list of key
+    sequences, and each key sequence must be a plist of symbols.
 
-    Minput_get_surrounding_text: When a callback function assigned for
-    this command is called, the first element of #MInputContext::plist
-    has key #Msymbol and the value specifies which portion of the
-    surrounding text should be retrieved.  If the value is positive,
-    it specifies the number of characters following the current cursor
-    position.  If the value is negative, the absolute value specifies
-    the number of characters preceding the current cursor position.
-    The callback function must set the key of this element to #Mtext
-    and the value to the retrieved M-text (whose length may be shorter
-    than the requested number of characters if the available text is
-    not that long, or it may be longer if an application thinks it's
-    more efficient to return that length).
+    If $KEYSEQLIST is an empty plist, any configuration and
+    customization of the command are cancelled, and default key
+    sequences become effective.
 
-    Minput_delete_surrounding_text: When a callback function assigned
-    for this command is called, the first element of
-    #MInputContext::plist has key #Msymbol and the value specifies
-    which portion of the surrounding text should be deleted in the
-    same way as the case of Minput_get_surrounding_text.  The callback
-    function must delete the specified text.  It should not alter
-    #MInputContext::plist.  */ 
+    If $KEYSEQLIST is NULL, the configuration of the command is
+    canceled, and the original key sequences (what saved in per-user
+    customization file, or the default one) become effective.
 
-/***ja
-    @name ÊÑ¿ô¡§ ¥³¡¼¥ë¥Ð¥Ã¥¯¥³¥Þ¥ó¥ÉÍÑÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë.
+    In the latter two cases, $COMMAND can be #Mnil to make all the
+    commands of the input method the target of the operation.
 
-    ÆþÎϥ᥽¥Ã¥É¥É¥é¥¤¥Ð¤Î¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤Ë¤ª¤¤¤Æ @c COMMAND 
-    °ú¿ô¤È¤·¤ÆÍѤ¤¤é¤ì¤ëÄêµÁºÑ¤ß¥·¥ó¥Ü¥ë (#MInputDriver::callback_list »²¾È)¡£
-      */ 
-/*** @{ */ 
-/*=*/
+    If $NAME is #Mnil, this function configures the key assignment of a
+    global command, not that of a specific input method.
 
-MSymbol Minput_preedit_start;
-MSymbol Minput_preedit_done;
-MSymbol Minput_preedit_draw;
-MSymbol Minput_status_start;
-MSymbol Minput_status_done;
-MSymbol Minput_status_draw;
-MSymbol Minput_candidates_start;
-MSymbol Minput_candidates_done;
-MSymbol Minput_candidates_draw;
-MSymbol Minput_set_spot;
-MSymbol Minput_toggle;
-MSymbol Minput_reset;
-MSymbol Minput_get_surrounding_text;
-MSymbol Minput_delete_surrounding_text;
-/*** @} */
+    The configuration takes effect for input methods opened or
+    re-opened later in the current session.  In order to make the
+    configuration take effect for the future session, it must be saved
+    in a per-user customization file by the function
+    minput_save_config ().
 
-/*=*/
+    @return
+    If the operation was successful, this function returns 0,
+    otherwise returns -1.  The operation fails in these cases:
+    <ul>
+    <li>$KEYSEQLIST is not in a valid form.
+    <li>$COMMAND is not available for the input method.
+    <li>$LANGUAGE and $NAME do not specify an existing input method.
+    </ul>
 
-/***en
-    @name Variables: Predefined symbols for special input events.
+    @seealso
+    minput_get_commands (), minput_save_config ().
+*/
+/***ja
+    @brief ÆþÎϥ᥽¥Ã¥É¤Î¥³¥Þ¥ó¥É¤Î¥­¡¼¥·¡¼¥¯¥¨¥ó¥¹¤òÀßÄꤹ¤ë.
 
-    These are the predefined symbols that are used as the @c KEY
-    argument of minput_filter ().  */ 
+    ´Ø¿ô minput_config_command () ¤Ï¥­¡¼¥·¡¼¥¯¥¨¥ó¥¹¤Î¥ê¥¹¥È
+    $KEYSEQLIST ¤ò¡¢$LANGUAGE ¤È $NAME ¤Ë¤è¤Ã¤Æ»ØÄꤵ¤ì¤ëÆþÎϥ᥽¥Ã¥É¤Î
+    ¥³¥Þ¥ó¥É $COMMAND ¤Ë³ä¤êÅö¤Æ¤ë¡£
 
-/*** @{ */ 
-/*=*/
+    $KEYSEQLIST ¤¬¶õ¥ê¥¹¥È¤Ç¤Ê¤±¤ì¤Ð¡¢¥­¡¼¥·¡¼¥¯¥¨¥ó¥¹¤Î¥ê¥¹¥È¤Ç¤¢¤ê¡¢
+    ³Æ¥­¡¼¥·¡¼¥¯¥¨¥ó¥¹¤Ï¥·¥ó¥Ü¥ë¤Î plist ¤Ç¤¢¤ë¡£
 
-MSymbol Minput_focus_out;
-MSymbol Minput_focus_in;
-MSymbol Minput_focus_move;
+    $KEYSEQLIST ¤¬¶õ¤Î plist ¤Ê¤é¤Ð¡¢¤½¤Î¥³¥Þ¥ó¥É¤ÎÀßÄê¤ä¥«¥¹¥¿¥Þ¥¤¥º¤Ï
+    ¤¹¤Ù¤Æ¥­¥ã¥ó¥»¥ë¤µ¤ì¡¢¥Ç¥Õ¥©¥ë¥È¤Î¥­¡¼¥·¡¼¥¯¥¨¥ó¥¹¤¬Í­¸ú¤Ë¤Ê¤ë¡£
 
-/*** @} */
+    $KEYSEQLIST ¤¬ NULL ¤Ç¤¢¤ì¤Ð¡¢¤½¤Î¥³¥Þ¥ó¥É¤ÎÀßÄê¤Ï¥­¥ã¥ó¥»¥ë¤µ¤ì¡¢
+    ¸µ¤Î¥­¡¼¥·¡¼¥¯¥¨¥ó¥¹¡Ê¥æ¡¼¥¶Ëè¤Î¥«¥¹¥¿¥Þ¥¤¥º¥Õ¥¡¥¤¥ë¤ËÊݸ¤µ¤ì¤Æ¤¤
+    ¤ë¤â¤Î¡¢¤¢¤ë¤¤¤Ï¥Ç¥Õ¥©¥ë¥È¤Î¤â¤Î¡Ë¤¬Í­¸ú¤Ë¤Ê¤ë¡£
 
-/*=*/
+    ¸å¤Î¤Õ¤¿¤Ä¤Î¾ì¹ç¤Ë¤Ï¡¢$COMMAND ¤Ï #Mnil ¤ò¤È¤ë¤³¤È¤¬¤Ç¤­¡¢»ØÄê¤ÎÆþ
+    Îϥ᥽¥Ã¥É¤ÎÁ´¤Æ¤Î¥³¥Þ¥ó¥ÉÀßÄê¤Î¥­¥ã¥ó¥»¥ë¤ò°ÕÌ£¤¹¤ë¡£
 
-/***en
-    @brief The default driver for internal input methods.
+    $NAME ¤¬ #Mnil ¤Ê¤é¤Ð¡¢¤³¤Î´Ø¿ô¤Ï¸Ä¡¹¤ÎÆþÎϥ᥽¥Ã¥É¤Ç¤Ï¤Ê¤¯¥°¥í¡¼¥Ð
+    ¥ë¤Ê¥³¥Þ¥ó¥É¤Î¥­¡¼³ä¤êÅö¤Æ¤òÀßÄꤹ¤ë¡£
 
-    The variable #minput_default_driver is the default driver for
-    internal input methods.
+    ¤³¤ì¤é¤ÎÀßÄê¤Ï¡¢¸½¹Ô¤Î¥»¥Ã¥·¥ç¥óÃæ¤ÇÆþÎϥ᥽¥Ã¥É¤¬¥ª¡¼¥×¥ó¡Ê¤Þ¤¿¤Ï
+    ºÆ¥ª¡¼¥×¥ó¡Ë¤µ¤ì¤¿»þÅÀ¤ÇÍ­¸ú¤Ë¤Ê¤ë¡£¾­Íè¤Î¥»¥Ã¥·¥ç¥óÃæ¤Ç¤âÍ­¸ú¤Ë¤¹
+    ¤ë¤¿¤á¤Ë¤Ï¡¢´Ø¿ô minput_save_config () ¤òÍѤ¤¤Æ¥æ¡¼¥¶Ëè¤Î¥«¥¹¥¿¥Þ¥¤
+    ¥º¥Õ¥¡¥¤¥ë¤ËÊݸ¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
 
-    The member MInputDriver::open_im () searches the m17n database for
-    an input method that matches the tag \< #Minput_method, $LANGUAGE,
-    $NAME\> and loads it.
+    @return
 
-    The member MInputDriver::callback_list () is @c NULL.  Thus, it is
-    programmers responsibility to set it to a plist of proper callback
-    functions.  Otherwise, no feedback information (e.g. preedit text)
-    can be shown to users.
+    ¤³¤Î´Ø¿ô¤Ï¡¢½èÍý¤¬À®¸ù¤¹¤ì¤Ð 0 ¤ò¡¢¼ºÇÔ¤¹¤ì¤Ð -1 ¤òÊÖ¤¹¡£¼ºÇԤȤϰʲ¼¤Î¾ì¹ç¤Ç¤¢¤ë¡£
+    <ul>
+    <li>$KEYSEQLIST ¤¬Í­¸ú¤Ê·Á¼°¤Ç¤Ê¤¤¡£
+    <li>$COMMAND ¤¬»ØÄê¤ÎÆþÎϥ᥽¥Ã¥É¤ÇÍøÍѤǤ­¤Ê¤¤¡£
+    <li>$LANGUAGE ¤È $NAME ¤Ç»ØÄꤵ¤ì¤ëÆþÎϥ᥽¥Ã¥É¤¬Â¸ºß¤·¤Ê¤¤¡£
+    </ul>
 
-    The macro M17N_INIT () sets the variable #minput_driver to the
-    pointer to this driver so that all internal input methods use it.
+    @seealso
+    minput_get_commands (), minput_save_config ().
+*/
 
-    Therefore, unless @c minput_driver is set differently, the driver
-    dependent arguments $ARG of the functions whose name begins with
-    "minput_" are all ignored.  */
+#if EXAMPLE_CODE
+/* Add "C-x u" to the "start" command of Unicode input method.  */
+{
+  MSymbol start_command = msymbol ("start");
+  MSymbol unicode = msymbol ("unicode");
+  MPlist *cmd, *plist, *key_seq_list, *key_seq;
 
-/***ja
-    @brief ÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѥǥե©¥ë¥È¥É¥é¥¤¥Ð.
+  /* At first get the current key-sequence assignment.  */
+  cmd = minput_get_command (Mt, unicode, start_command);
+  if (! cmd)
+    {
+      /* The input method does not have the command "start".  Here
+        should come some error handling code.  */
+    }
+  /* Now CMD == ((start DESCRIPTION STATUS KEY-SEQUENCE ...) ...).
+     Extract the part (KEY-SEQUENCE ...).  */
+  plist = mplist_next (mplist_next (mplist_next (mplist_value (cmd))));
+  /* Copy it because we should not modify it directly.  */
+  key_seq_list = mplist_copy (plist);
+  
+  key_seq = mplist ();
+  mplist_add (key_seq, Msymbol, msymbol ("C-x"));
+  mplist_add (key_seq, Msymbol, msymbol ("u"));
+  mplist_add (key_seq_list, Mplist, key_seq);
+  m17n_object_unref (key_seq);
+
+  minput_config_command (Mt, unicode, start_command, key_seq_list);
+  m17n_object_unref (key_seq_list);
+}
+#endif
 
-    ÊÑ¿ô #minput_default_driver ¤ÏÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѤΥǥե©¥ë¥È¤Î¥É¥é¥¤¥Ð¤òɽ¤¹¡£
+int
+minput_config_command (MSymbol language, MSymbol name, MSymbol command,
+                      MPlist *keyseqlist)
+{
+  MInputMethodInfo *im_info, *config;
+  MPlist *plist;
 
-    ¥á¥ó¥Ð MInputDriver::open_im () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é¥¿¥° 
-    \< #Minput_method, $LANGUAGE, $NAME\> 
-    ¤Ë¹çÃפ¹¤ëÆþÎϥ᥽¥Ã¥É¤òõ¤·¡¢¤½¤ì¤ò¥í¡¼¥É¤¹¤ë¡£
+  MINPUT__INIT ();
 
-    ¥á¥ó¥Ð MInputDriver::callback_list () ¤Ï @c NULL ¤Ç¤¢¤ê¡¢
-    ¤·¤¿¤¬¤Ã¤Æ¡¢¥×¥í¥°¥é¥Þ¦¤ÇÀÕǤ¤ò»ý¤Ã¤Æ Å¬Àڤʥ³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤Î plist
-    ¤ËÀßÄꤷ¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¤µ¤â¤Ê¤¤¤È¡¢preedit 
-    ¥Æ¥­¥¹¥È¤Ê¤É¤Î¥Õ¥£¡¼¥É¥Ð¥Ã¥¯¾ðÊ󤬥桼¥¶¤Ëɽ¼¨¤µ¤ì¤Ê¤¤¡£
+  im_info = get_im_info (language, name, Mnil, Mcommand);
+  if (! im_info)
+    MERROR (MERROR_IM, -1);
+  if (command == Mnil ? (keyseqlist && ! MPLIST_TAIL_P (keyseqlist))
+      : (! im_info->cmds
+        || ! mplist__assq (im_info->configured_cmds, command)))
+    MERROR (MERROR_IM, -1);
+  if (keyseqlist && ! MPLIST_TAIL_P (keyseqlist))
+    {
+      MPLIST_DO (plist, keyseqlist)
+       if (! check_command_keyseq (plist))
+         MERROR (MERROR_IM, -1);
+    }
 
-    ¥Þ¥¯¥í M17N_INIT () ¤ÏÊÑ¿ô #minput_driver 
-    ¤ò¤³¤Î¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤ËÀßÄꤷ¡¢Á´¤Æ¤ÎÆâÉôÆþÎϥ᥽¥Ã¥É¤¬¤³¤Î¥É¥é¥¤¥Ð¤ò»È¤¦¤è¤¦¤Ë¤¹¤ë¡£
+  config = get_config_info (im_info);
+  if (! config)
+    {
+      if (! im_config_list)
+       im_config_list = mplist ();
+      config = new_im_info (NULL, language, name, Mnil, im_config_list);
+      config->cmds = mplist ();
+      config->vars = mplist ();
+    }
 
-    ¤·¤¿¤¬¤Ã¤Æ¡¢@c minput_driver ¤¬¥Ç¥Õ¥©¥ë¥ÈÃͤΤޤޤǤ¢¤ì¤Ð¡¢minput_ 
-    ¤Ç»Ï¤Þ¤ë´Ø¿ô¤Î¥É¥é¥¤¥Ð¤Ë°Í¸¤¹¤ë°ú¿ô $ARG ¤Ï¤¹¤Ù¤Æ̵»ë¤µ¤ì¤ë¡£  */
+  if (! keyseqlist && MPLIST_TAIL_P (config->cmds))
+    /* Nothing to do.  */
+    return 0;
+
+  if (command == Mnil)
+    {
+      if (! keyseqlist)
+       {
+         /* Cancal the configuration. */
+         if (MPLIST_TAIL_P (config->cmds))
+           return 0;
+         mplist_set (config->cmds, Mnil, NULL);
+       }
+      else
+       {
+         /* Cancal the customization. */
+         MInputMethodInfo *custom = get_custom_info (im_info);
+
+         if (MPLIST_TAIL_P (config->cmds)
+             && (! custom || ! custom->cmds || MPLIST_TAIL_P (custom->cmds)))
+           /* Nothing to do.  */
+           return 0;
+         mplist_set (config->cmds, Mnil, NULL);
+         MPLIST_DO (plist, custom->cmds)
+           {
+             command = MPLIST_SYMBOL (MPLIST_PLIST (plist));
+             plist = mplist ();
+             mplist_add (plist, Msymbol, command);
+             mplist_push (config->cmds, Mplist, plist);
+             M17N_OBJECT_UNREF (plist);
+           }
+       }
+    }
+  else
+    {
+      plist = mplist__assq (config->cmds, command);
+      if (! keyseqlist)
+       {
+         /* Cancel the configuration.  */
+         if (! plist)
+           return 0;
+         mplist__pop_unref (plist);
+       }
+      else if (MPLIST_TAIL_P (keyseqlist))
+       {
+         /* Cancel the customization.  */
+         MInputMethodInfo *custom = get_custom_info (im_info);
+         int no_custom = (! custom || ! custom->cmds
+                          || ! mplist__assq (custom->cmds, command));
+         if (! plist)
+           {
+             if (no_custom)
+               return 0;
+             plist = mplist ();
+             mplist_add (config->cmds, Mplist, plist);
+             M17N_OBJECT_UNREF (plist);
+             plist = mplist_add (plist, Msymbol, command);
+           }
+         else
+           {
+             if (no_custom)
+               mplist__pop_unref (plist);
+             else
+               {
+                 plist = MPLIST_PLIST (plist); /* (NAME nil KEYSEQ ...) */
+                 plist = MPLIST_NEXT (plist);
+                 mplist_set (plist, Mnil, NULL);
+               }
+           }
+       }
+      else
+       {
+         MPlist *pl;
+
+         if (plist)
+           {
+             plist = MPLIST_NEXT (MPLIST_PLIST (plist));
+             if (! MPLIST_TAIL_P (plist))
+               mplist_set (plist, Mnil, NULL);
+           }
+         else
+           {
+             plist = mplist ();
+             mplist_add (config->cmds, Mplist, plist);
+             M17N_OBJECT_UNREF (plist);
+             plist = mplist_add (plist, Msymbol, command);
+             plist = MPLIST_NEXT (plist);
+           }
+         MPLIST_DO (keyseqlist, keyseqlist)
+           {
+             pl = mplist_copy (MPLIST_VAL (keyseqlist));
+             plist = mplist_add (plist, Mplist, pl);
+             M17N_OBJECT_UNREF (pl);
+           }
+       }
+    }
+  config_all_commands (im_info);
+  im_info->tick = time (NULL);
+  return 0;
+}
 
-MInputDriver minput_default_driver;
 /*=*/
 
 /***en
-    @brief The driver for internal input methods.
+    @brief Get information about input method variable(s).
 
-    The variable #minput_driver is a pointer to the input method
-    driver that is used by internal input methods.  The macro
-    M17N_INIT () initializes it to a pointer to #minput_default_driver
-    if <m17n<EM></EM>.h> is included.  */ 
-/***ja
-    @brief ÆâÉôÆþÎϥ᥽¥Ã¥ÉÍѥɥ饤¥Ð.
+    The minput_get_variable () function returns information about
+    variable $VARIABLE of the input method specified by $LANGUAGE and $NAME.
+    An input method variable controls behavior of an input method.
 
-    ÊÑ¿ô #minput_driver ¤ÏÆâÉôÆþÎϥ᥽¥Ã¥É¤Ë¤è¤Ã¤Æ»ÈÍѤµ¤ì¤Æ¤¤¤ëÆþÎÏ¥á
-    ¥½¥Ã¥É¥É¥é¥¤¥Ð¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¥Þ¥¯¥í M17N_INIT () ¤Ï¤³¤Î¥Ý¥¤¥ó
-    ¥¿¤ò#minput_default_driver (<m17n<EM></EM>.h> ¤¬ include ¤µ¤ì¤Æ¤¤¤ë
-    »þ) ¤Ë½é´ü²½¤¹¤ë¡£  */ 
+    There are two kinds of variables, global and local.  A global
+    variable has a global definition, and the description and the value
+    may be inherited by a local variable.  Each input method defines a
+    local variable which has local value.  It may also declare a
+    local variable that inherits definition of a global variable of
+    the same name.
 
-MInputDriver *minput_driver;
+    If $LANGUAGE is #Mt and $NAME is #Mnil, information about a global
+    variable is returned.  Otherwise information about a local variable
+    is returned.
 
-MSymbol Minput_driver;
+    If $VARIABLE is #Mnil, information about all variables is
+    returned.
 
-/*=*/
+    The return value is a @e well-formed plist (@ref m17nPlist) of this
+    format:
+@verbatim
+  ((NAME DESCRIPTION STATUS VALUE [VALID-VALUE ...]) ...)
+@endverbatim
+    @c NAME is a symbol representing the variable name.
+
+    @c DESCRIPTION is an M-text describing the variable, or #Mnil if the
+    variable has no description.
+
+    @c STATUS is a symbol representing how the value is decided.  The
+    value is #Mnil (the default value), @b Mcustomized (the value is
+    customized by per-user customization file), or @b Mconfigured (the
+    value is set by the call of minput_config_variable ()).  For a
+    local variable only, it may also be @b Minherited (the value is
+    inherited from the corresponding global variable).
+
+    @c VALUE is the initial value of the variable.  If the key of this
+    element is #Mt, the variable has no initial value.  Otherwise, the
+    key is #Minteger, #Msymbol, or #Mtext and the value is of the
+    corresponding type.
+
+    @c VALID-VALUEs (if any) specify which values the variable can have.
+    They have the same type (i.e. having the same key) as @c VALUE except
+    for the case that VALUE is an integer.  In that case, @c VALID-VALUE
+    may be a plist of two integers specifying the range of possible
+    values.
 
-/***en
-    @brief Open an input method.
+    If there no @c VALID-VALUE, the variable can have any value as long
+    as the type is the same as @c VALUE.
 
-    The minput_open_im () function opens an input method whose
-    language and name match $LANGUAGE and $NAME, and returns a pointer
-    to the input method object newly allocated.
+    If $VARIABLE is not #Mnil, the first element of the returned plist
+    contains the information about $VARIABLE.
 
-    This function at first decides a driver for the input method as
-    described below.
+    @return
 
-    If $LANGUAGE is not #Mnil, the driver pointed by the variable
-    #minput_driver is used.
+    If the requested information was found, a pointer to a non-empty
+    plist is returned.  As the plist is kept in the library, the
+    caller must not modify nor free it.
 
-    If $LANGUAGE is #Mnil and $NAME has the property #Minput_driver, the
-    driver pointed to by the property value is used to open the input
-    method.  If $NAME has no such a property, @c NULL is returned.
+    Otherwise (the specified input method or the specified variable
+    does not exist), @c NULL is returned.  */
+/***ja
+    @brief ÆþÎϥ᥽¥Ã¥É¤ÎÊÑ¿ô¤Ë´Ø¤¹¤ë¾ðÊó¤òÆÀ¤ë.
+
+    ´Ø¿ô minput_get_variable () ¤Ï¡¢$LANGUAGE ¤È $NAME ¤Ç»ØÄꤵ¤ì¤ëÆþÎÏ
+    ¥á¥½¥Ã¥É¤ÎÊÑ¿ô $VARIABLE ¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÖ¤¹¡£ÆþÎϥ᥽¥Ã¥É¤ÎÊÑ¿ô¤È¤Ï¡¢
+    ÆþÎϥ᥽¥Ã¥É¤Î¿¶Éñ¤òÀ©¸æ¤¹¤ë¤â¤Î¤Ç¤¢¤ë¡£
+
+    ÊÑ¿ô¤Ë¤Ï¡¢¥°¥í¡¼¥Ð¥ë¤È¥í¡¼¥«¥ë¤Î£²¼ïÎब¤¢¤ë¡£¥°¥í¡¼¥Ð¥ë¤ÊÊÑ¿ô¤Ï¥°
+    ¥í¡¼¥Ð¥ë¤ËÄêµÁ¤µ¤ì¡¢¥í¡¼¥«¥ë¤ÊÊÑ¿ô¤Ï¤½¤ÎÀâÌÀ¤ÈÃͤò·Ñ¾µ¤¹¤ë¤³¤È¤¬¤Ç
+    ¤­¤ë¡£³ÆÆþÎϥ᥽¥Ã¥É¤Ï¥í¡¼¥«¥ë¤ÊÃͤò»ý¤Ä¥í¡¼¥«¥ë¤ÊÊÑ¿ô¤òÄêµÁ¤¹¤ë¡£
+    ¤Þ¤¿Æ±Ì¾¤Î¥°¥í¡¼¥Ð¥ë¤ÊÊÑ¿ô¤ÎÄêµÁ¤ò·Ñ¾µ¤¹¤ë¥í¡¼¥«¥ë¤ÊÊÑ¿ô¤òÀë¸À¤¹¤ë
+    ¤³¤È¤â¤Ç¤­¤ë¡£
+
+    $LANGUAGE ¤¬ #Mt ¤Ç $NAME ¤¬ #Mnil ¤Î¾ì¹ç¤Ï¡¢¤³¤Î´Ø¿ô¤Ï¥°¥í¡¼¥Ð¥ëÊÑ
+    ¿ô¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¥í¡¼¥«¥ëÊÑ¿ô¤Ë´Ø¤¹¤ë¤â¤Î¤òÊÖ¤¹¡£
+
+    $VARIABLE ¤¬ #Mnil ¤Î¾ì¹ç¤Ï¡¢¤¹¤Ù¤Æ¤Î¥³¥Þ¥ó¥É¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÖ¤¹¡£
+
+    Ìá¤êÃͤϰʲ¼¤Î·Á¼°¤Î @e well-formed plist (@ref m17nPlist) ¤Ç¤¢¤ë¡£
+@verbatim
+  ((NAME DESCRIPTION STATUS VALUE [VALID-VALUE ...]) ...)
+@endverbatim
 
-    Then, the member MInputDriver::open_im () of the driver is
-    called.  
+    @c NAME ¤ÏÊÑ¿ô¤Î̾Á°¤ò¼¨¤¹¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£
 
-    $ARG is set in the member @c arg of the structure MInputMethod so
-    that the driver can refer to it.  */
+    @c DESCRIPTION ¤ÏÊÑ¿ô¤òÀâÌÀ¤¹¤ë M-text ¤Ç¤¢¤ë¤«¡¢ÀâÌÀ¤¬Ìµ¤¤¾ì¹ç¤Ë¤Ï
+    #Mnil ¤Ç¤¢¤ë¡£
 
-/***ja
-    @brief ÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤¹¤ë.
+    @c STATUS ¤ÏÃͤ¬¤É¤Î¤è¤¦¤ËÄê¤á¤é¤ì¤ë¤«¤ò¤¢¤é¤ï¤¹¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢
+    @c STATUS ¤ÎÃͤϠ#Mnil ¡Ê¥Ç¥Õ¥©¥ë¥È¤ÎÃÍ¡Ë, @b Mcustomized ¡Ê¥æ¡¼¥¶Ëè¤Î
+    ¥«¥¹¥¿¥Þ¥¤¥º¥Õ¥¡¥¤¥ë¤Ë¤è¤Ã¤Æ¥«¥¹¥¿¥Þ¥¤¥º¤µ¤ì¤¿ÃÍ¡Ë, @b Mconfigured
+    ¡Êminput_config_variable ()¤ò¸Æ¤Ö¤³¤È¤Ë¤è¤Ã¤ÆÀßÄꤵ¤ì¤ëÃ͡ˤΤ¤¤º¤ì
+    ¤«¤Ç¤¢¤ë¡£¥í¡¼¥«¥ëÊÑ¿ô¤Î¾ì¹ç¤Ë¤Ï¡¢@b Minherited ¡ÊÂбþ¤¹¤ë¥°¥í¡¼¥Ð¥ë
+    ÊÑ¿ô¤«¤é·Ñ¾µ¤·¤¿Ã͡ˤǤâ¤è¤¤¡£
 
-    ´Ø¿ô minput_open_im () ¤Ï¸À¸ì $LANGUAGE ¤È̾Á° $NAME 
-    ¤Ë¹çÃפ¹¤ëÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤·¡¢¿·¤¿¤Ë³ä¤êÅö¤Æ¤é¤ì¤¿ÆþÎϥ᥽¥Ã¥É¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
-    
-    ¤³¤Î´Ø¿ô¤Ï¡¢¤Þ¤ºÆþÎϥ᥽¥Ã¥ÉÍѤΥɥ饤¥Ð¤ò°Ê²¼¤Î¤è¤¦¤Ë¤·¤Æ·èÄꤹ¤ë¡£
+    @c VALUE ¤ÏÊÑ¿ô¤Î½é´üÃͤǤ¢¤ë¡£¤³¤ÎÍ×ÁǤΥ­¡¼¤¬#Mt ¤Ç¤¢¤ì¤Ð½é´üÃͤò»ý
+    ¤¿¤Ê¤¤¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢¥­¡¼¤Ï #Minteger, #Msymbol, #Mtext ¤Î¤¤¤º¤ì
+    ¤«¤Ç¤¢¤ê¡¢ÃͤϤ½¤ì¤¾¤ìÂбþ¤¹¤ë·¿¤Î¤â¤Î¤Ç¤¢¤ë¡£
 
-    $LANGUAGE ¤¬ #Mnil ¤Ç¤Ê¤±¤ì¤Ð¡¢ÊÑ¿ô #minput_driver 
-    ¤Ç»Ø¤µ¤ì¤Æ¤¤¤ë¥É¥é¥¤¥Ð¤òÍѤ¤¤ë¡£
+    @c VALID-VALUE ¤Ï¤â¤·¤¢¤ì¤Ð¡¢ÊÑ¿ô¤Î¼è¤êÆÀ¤ëÃͤò»ØÄꤹ¤ë¡£¤³¤ì¤Ï @c VALUE
+    ¤ÈƱ¤¸·¿(¤¹¤Ê¤ï¤ÁƱ¤¸¥­¡¼¤ò»ý¤Ä) ¤Ç¤¢¤ë¤¬¡¢Îã³°¤È¤·¤Æ @c VALUE ¤¬
+    integer ¤Î¾ì¹ç¤Ï @c VALID-VALUE ¤Ï²Äǽ¤ÊÃͤÎÈϰϤò¼¨¤¹Æó¤Ä¤ÎÀ°¿ô¤«¤é
+    ¤Ê¤ë plist ¤È¤Ê¤ë¤³¤È¤¬¤Ç¤­¤ë¡£
 
-    $LANGUAGE ¤¬ #Mnil ¤Ç¤¢¤ê¡¢$NAME ¤¬ #Minput_driver
-    ¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¾ì¹ç¤Ë¤Ï¡¢¤½¤Î¥×¥í¥Ñ¥Æ¥£¤ÎÃͤǻؤµ¤ì¤Æ¤¤¤ëÆþÎϥɥ饤¥Ð¤òÍѤ¤¤ÆÆþÎϥ᥽¥Ã¥É¤ò¥ª¡¼¥×¥ó¤¹¤ë¡£
-    $NAME ¤Ë¤½¤Î¤è¤¦¤Ê¥×¥í¥Ñ¥Æ¥£¤¬Ìµ¤«¤Ã¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£
+    @c VALID-VALUE ¤¬¤Ê¤±¤ì¤Ð¡¢ÊÑ¿ô¤Ï @c VALUE ¤ÈƱ¤¸·¿¤Ç¤¢¤ë¸Â¤ê¤¤¤«¤Ê¤ëÃͤâ
+    ¤È¤ë¤³¤È¤¬¤Ç¤­¤ë¡£
 
-    ¼¡¤¤¤Ç¡¢¥É¥é¥¤¥Ð¤Î¥á¥ó¥Ð MInputDriver::open_im () ¤¬¸Æ¤Ð¤ì¤ë¡£
+    $VARIABLE ¤¬ #Mnil ¤Ç¤Ê¤±¤ì¤Ð¡¢ÊÖ¤µ¤ì¤ë plist ¤ÎºÇ½é¤ÎÍ×ÁǤÏ
+    $VARIABLE ¤Ë´Ø¤¹¤ë¾ðÊó¤ò´Þ¤à¡£
 
-    $ARG ¤Ï¹½Â¤ÂΠMInputMethod ¤Î¥á¥ó¥Ð @c arg ¤ËÀßÄꤵ¤ì¡¢¥É¥é¥¤¥Ð¤«¤é»²¾È¤Ç¤­¤ë¡£
+    @return
 
-    @latexonly \IPAlabel{minput_open} @endlatexonly
+    µá¤á¤é¤ì¤¿¾ðÊ󤬸«¤Ä¤«¤ì¤Ð¡¢¶õ¤Ç¤Ê¤¤ plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¥ê¥¹
+    ¥È¤Ï¥é¥¤¥Ö¥é¥ê¤¬´ÉÍý¤·¤Æ¤¤¤ë¤Î¤Ç¡¢¸Æ½Ð¦¤¬Êѹ¹¤·¤¿¤ê²òÊü¤·¤¿¤ê¤¹¤ë
+    ¤³¤È¤Ï¤Ç¤­¤Ê¤¤¡£
 
-*/
+    ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð¡¢¤¹¤Ê¤ï¤Á»ØÄê¤ÎÆþÎϥ᥽¥Ã¥É¤äÊÑ¿ô¤¬Â¸ºß¤·¤Ê¤±¤ì¤Ð
+    @c NULL ¤òÊÖ¤¹¡£ */
 
-MInputMethod *
-minput_open_im (MSymbol language, MSymbol name, void *arg)
+MPlist *
+minput_get_variable (MSymbol language, MSymbol name, MSymbol variable)
 {
-  MInputMethod *im;
-  MInputDriver *driver;
+  MInputMethodInfo *im_info;
 
-  MDEBUG_PRINT2 ("  [IM] opening (%s %s) ... ",
-        msymbol_name (language), msymbol_name (name));
-  if (language)
-    driver = minput_driver;
-  else
-    {
-      driver = (MInputDriver *) msymbol_get (name, Minput_driver);
-      if (! driver)
-       MERROR (MERROR_IM, NULL);
-    }
+  MINPUT__INIT ();
 
-  MSTRUCT_CALLOC (im, MERROR_IM);
-  im->language = language;
-  im->name = name;
-  im->arg = arg;
-  im->driver = *driver;
-  if ((*im->driver.open_im) (im) < 0)
-    {
-      MDEBUG_PRINT (" failed\n");
-      free (im);
-      return NULL;
-    }
-  MDEBUG_PRINT (" ok\n");
-  return im;
+  im_info = get_im_info (language, name, Mnil, Mvariable);
+  if (! im_info || ! im_info->configured_vars)
+    return NULL;
+  if (variable == Mnil)
+    return im_info->configured_vars;
+  return mplist__assq (im_info->configured_vars, variable);
 }
 
 /*=*/
 
 /***en
-    @brief Close an input method.
+    @brief Configure the value of an input method variable.
 
-    The minput_close_im () function closes the input method $IM, which
-    must have been created by minput_open_im ().  */
+    The minput_config_variable () function assigns $VALUE to the
+    variable $VARIABLE of the input method specified by $LANGUAGE and
+    $NAME.
 
-/***ja
-    @brief ÆþÎϥ᥽¥Ã¥É¤ò¥¯¥í¡¼¥º¤¹¤ë.
+    If $VALUE is a non-empty plist, it must be a plist of one element
+    whose key is #Minteger, #Msymbol, or #Mtext, and the value is of
+    the corresponding type.  That value is assigned to the variable.
 
-    ´Ø¿ô minput_close_im () ¤Ï¡¢ÆþÎϥ᥽¥Ã¥É $IM ¤ò¥¯¥í¡¼¥º¤¹¤ë¡£
-    ¤³¤ÎÆþÎϥ᥽¥Ã¥É $IM ¤Ï minput_open_im () ¤Ë¤è¤Ã¤Æºî¤é¤ì¤¿¤â¤Î¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£  */
+    If $VALUE is an empty plist, any configuration and customization
+    of the variable are canceled, and the default value is assigned to
+    the variable.
 
-void
-minput_close_im (MInputMethod *im)
-{
-  MDEBUG_PRINT2 ("  [IM] closing (%s %s) ... ",
-                msymbol_name (im->name), msymbol_name (im->language));
-  (*im->driver.close_im) (im);
-  free (im);
-  MDEBUG_PRINT (" done\n");
-}
+    If $VALUE is NULL, the configuration of the variable is canceled,
+    and the original value (what saved in per-user customization file,
+    or the default value) is assigned to the variable.
 
-/*=*/
+    In the latter two cases, $VARIABLE can be #Mnil to make all the
+    variables of the input method the target of the operation.
 
-/***en
-    @brief Create an input context.
+    If $NAME is #Mnil, this function configures the value of global
+    variable, not that of a specific input method.
 
-    The minput_create_ic () function creates an input context object
-    associated with input method $IM, and calls callback functions
-    corresponding to #Minput_preedit_start, #Minput_status_start, and
-    #Minput_status_draw in this order.
+    The configuration takes effect for input methods opened or
+    re-opened later in the current session.  To make the configuration
+    take effect for the future session, it must be saved in a per-user
+    customization file by the function minput_save_config ().
 
     @return
-    If an input context is successfully created, minput_create_ic ()
-    returns a pointer to it.  Otherwise it returns @c NULL.  */
 
+    If the operation was successful, this function returns 0,
+    otherwise returns -1.  The operation fails in these cases:
+    <ul>
+    <li>$VALUE is not in a valid form, the type does not match the
+    definition, or the value is our of range.
+    <li>$VARIABLE is not available for the input method.
+    <li>$LANGUAGE and $NAME do not specify an existing input method.  
+    </ul>
+
+    @seealso
+    minput_get_variable (), minput_save_config ().  */
 /***ja
-    @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤òÀ¸À®¤¹¤ë.
+    @brief ÆþÎϥ᥽¥Ã¥É¤ÎÊÑ¿ô¤ÎÃͤòÀßÄꤹ¤ë.
 
-    ´Ø¿ô minput_create_ic () ¤ÏÆþÎϥ᥽¥Ã¥É $IM
-    ¤ËÂбþ¤¹¤ëÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¥ª¥Ö¥¸¥§¥¯¥È¤òÀ¸À®¤·¡¢
-    #Minput_preedit_start, #Minput_status_start, #Minput_status_draw
-    ¤ËÂбþ¤¹¤ë¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤ò¤³¤Î½ç¤Ë¸Æ¤Ö¡£
+    ´Ø¿ô minput_config_variable () ¤ÏÃÍ $VALUE ¤ò¡¢$LANGUAGE ¤È $NAME
+    ¤Ë¤è¤Ã¤Æ»ØÄꤵ¤ì¤ëÆþÎϥ᥽¥Ã¥É¤ÎÊÑ¿ô $VARIABLE ¤Ë³ä¤êÅö¤Æ¤ë¡£
+
+    $VALUE ¤¬ ¶õ¥ê¥¹¥È¤Ç¤Ê¤±¤ì¤Ð¡¢£±Í×ÁǤΠplist ¤Ç¤¢¤ê¡¢¤½¤Î¥­¡¼¤Ï
+    #Minteger, #Msymbol, #Mtext ¤Î¤¤¤º¤ì¤«¡¢ÃͤÏÂбþ¤¹¤ë·¿¤Î¤â¤Î¤Ç¤¢¤ë¡£
+    ¤³¤ÎÃͤ¬ÊÑ¿ô $VARIABLE ¤Ë³ä¤êÅö¤Æ¤é¤ì¤ë¡£
+
+    $VALUE ¤¬ ¶õ¥ê¥¹¥È¤Ç¤¢¤ì¤Ð¡¢ÊÑ¿ô¤ÎÀßÄê¤È¥«¥¹¥¿¥Þ¥¤¥º¤¬¥­¥ã¥ó¥»¥ë¤µ
+    ¤ì¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤ¬ÊÑ¿ô $VARIABLE ¤Ë³ä¤êÅö¤Æ¤é¤ì¤ë¡£
+
+    $VALUE ¤¬ NULL ¤Ç¤¢¤ì¤Ð¡¢ÊÑ¿ô¤ÎÀßÄê¤Ï¥­¥ã¥ó¥»¥ë¤µ¤ì¡¢¸µ¤ÎÃ͡ʥ桼¥¶
+    Ëè¤Î¥«¥¹¥¿¥Þ¥¤¥º¥Õ¥¡¥¤¥ëÃæ¤ÎÃÍ¡¢¤Þ¤¿¤Ï¥Ç¥Õ¥©¥ë¥È¤ÎÃ͡ˤ¬³ä¤êÅö¤Æ¤é¤ì¤ë¡£
+
+    ¸å¤Î¤Õ¤¿¤Ä¤Î¾ì¹ç¤Ë¤Ï¡¢$VARIABLE ¤Ï #Mnil ¤ò¤È¤ë¤³¤È¤¬¤Ç¤­¡¢»ØÄꤵ¤ì
+    ¤¿ÆþÎϥ᥽¥Ã¥É¤ÎÁ´¤Æ¤ÎÊÑ¿ôÀßÄê¤Î¥­¥ã¥ó¥»¥ë¤ò°ÕÌ£¤¹¤ë¡£
+
+    $NAME ¤¬ #Mnil ¤Ê¤é¤Ð¡¢¤³¤Î´Ø¿ô¤Ï¸Ä¡¹¤ÎÆþÎϥ᥽¥Ã¥É¤Ç¤Ï¤Ê¤¯¥°¥í¡¼¥Ð
+    ¥ë¤ÊÊÑ¿ô¤ÎÃͤòÀßÄꤹ¤ë¡£
+
+    ¤³¤ì¤é¤ÎÀßÄê¤Ï¡¢¸½¹Ô¤Î¥»¥Ã¥·¥ç¥óÃæ¤ÇÆþÎϥ᥽¥Ã¥É¤¬¥ª¡¼¥×¥ó¡Ê¤Þ¤¿¤Ï
+    ºÆ¥ª¡¼¥×¥ó¡Ë¤µ¤ì¤¿»þÅÀ¤ÇÍ­¸ú¤Ë¤Ê¤ë¡£¾­Íè¤Î¥»¥Ã¥·¥ç¥óÃæ¤Ç¤âÍ­¸ú¤Ë¤¹
+    ¤ë¤¿¤á¤Ë¤Ï¡¢´Ø¿ô minput_save_config () ¤òÍѤ¤¤Æ¥æ¡¼¥¶Ëè¤Î¥«¥¹¥¿¥Þ¥¤
+    ¥º¥Õ¥¡¥¤¥ë¤ËÊݸ¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
 
     @return
-    ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤¬À¸À®¤µ¤ì¤¿¾ì¹ç¡¢minput_create_ic () 
-    ¤Ï¤½¤ÎÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£
-      */
 
-MInputContext *
-minput_create_ic (MInputMethod *im, void *arg)
+    ¤³¤Î´Ø¿ô¤Ï¡¢½èÍý¤¬À®¸ù¤¹¤ì¤Ð 0 ¤ò¡¢¼ºÇÔ¤¹¤ì¤Ð -1 ¤òÊÖ¤¹¡£¼ºÇԤȤϰʲ¼¤Î¾ì¹ç¤Ç¤¢¤ë¡£
+    <ul>
+    <li>$VALUE¤¬Í­¸ú¤Ê·Á¼°¤Ç¤Ê¤¤¡£·¿¤¬ÄêµÁ¤Ë¹ç¤ï¤Ê¤¤¡¢¤Þ¤¿¤ÏÃͤ¬Èϰϳ°¤Ç¤¢¤ë¡£
+    <li>$VARIABLE ¤¬»ØÄê¤ÎÆþÎϥ᥽¥Ã¥É¤ÇÍøÍѤǤ­¤Ê¤¤¡£
+    <li>$LANGUAGE ¤È $NAME ¤Ç»ØÄꤵ¤ì¤ëÆþÎϥ᥽¥Ã¥É¤¬Â¸ºß¤·¤Ê¤¤¡£
+    </ul>
+
+    @seealso
+    minput_get_commands (), minput_save_config ().
+*/
+int
+minput_config_variable (MSymbol language, MSymbol name, MSymbol variable,
+                       MPlist *value)
 {
-  MInputContext *ic;
+  MInputMethodInfo *im_info, *config;
+  MPlist *plist;
 
-  MDEBUG_PRINT2 ("  [IM] creating context (%s %s) ... ",
-                msymbol_name (im->name), msymbol_name (im->language));
-  MSTRUCT_CALLOC (ic, MERROR_IM);
-  ic->im = im;
-  ic->arg = arg;
-  ic->preedit = mtext ();
-  ic->candidate_list = NULL;
-  ic->produced = mtext ();
-  ic->spot.x = ic->spot.y = 0;
-  ic->active = 1;
-  ic->plist = mplist ();
-  if ((*im->driver.create_ic) (ic) < 0)
+  MINPUT__INIT ();
+
+  im_info = get_im_info (language, name, Mnil, Mvariable);
+  if (! im_info)
+    MERROR (MERROR_IM, -1);
+  if (variable == Mnil ? (value && ! MPLIST_TAIL_P (value))
+      : (! im_info->vars
+        || ! (plist = mplist__assq (im_info->configured_vars, variable))))
+    MERROR (MERROR_IM, -1);
+
+  if (value && ! MPLIST_TAIL_P (value))
     {
-      MDEBUG_PRINT (" failed\n");
-      M17N_OBJECT_UNREF (ic->preedit);
-      M17N_OBJECT_UNREF (ic->produced);
-      M17N_OBJECT_UNREF (ic->plist);
-      free (ic);
-      return NULL;
-    };
+      plist = MPLIST_PLIST (plist);
+      plist = MPLIST_NEXT (plist); /* (DESC STATUS VALUE VALIDS ...) */
+      plist = MPLIST_NEXT (plist); /* (STATUS VALUE VALIDS ...) */
+      plist = MPLIST_NEXT (plist); /* (VALUE VALIDS ...) */
+      if (MPLIST_KEY (plist) != Mt
+         && ! check_variable_value (value, plist))
+       MERROR (MERROR_IM, -1);
+    }
 
-  if (im->driver.callback_list)
+  config = get_config_info (im_info);
+  if (! config)
     {
-      minput__callback (ic, Minput_preedit_start);
-      minput__callback (ic, Minput_status_start);
-      minput__callback (ic, Minput_status_draw);
+      if (! im_config_list)
+       im_config_list = mplist ();
+      config = new_im_info (NULL, language, name, Mnil, im_config_list);
+      config->cmds = mplist ();
+      config->vars = mplist ();
     }
 
-  MDEBUG_PRINT (" ok\n");
-  return ic;
+  if (! value && MPLIST_TAIL_P (config->vars))
+    /* Nothing to do.  */
+    return 0;
+
+  if (variable == Mnil)
+    {
+      if (! value)
+       {
+         /* Cancel the configuration.  */
+         if (MPLIST_TAIL_P (config->vars))
+           return 0;
+         mplist_set (config->vars, Mnil, NULL);
+       }
+      else
+       {
+         /* Cancel the customization.  */
+         MInputMethodInfo *custom = get_custom_info (im_info);
+
+         if (MPLIST_TAIL_P (config->vars)
+             && (! custom || ! custom->vars || MPLIST_TAIL_P (custom->vars)))
+           /* Nothing to do.  */
+           return 0;
+         mplist_set (config->vars, Mnil, NULL);
+         MPLIST_DO (plist, custom->vars)
+           {
+             variable = MPLIST_SYMBOL (MPLIST_PLIST (plist));
+             plist = mplist ();
+             mplist_add (plist, Msymbol, variable);
+             mplist_push (config->vars, Mplist, plist);
+             M17N_OBJECT_UNREF (plist);
+           }
+       }
+    }
+  else
+    {
+      plist = mplist__assq (config->vars, variable);
+      if (! value)
+       {
+         /* Cancel the configuration.  */
+         if (! plist)
+           return 0;
+         mplist__pop_unref (plist);
+       }
+      else if (MPLIST_TAIL_P (value))
+       {
+         /* Cancel the customization.  */
+         MInputMethodInfo *custom = get_custom_info (im_info);
+         int no_custom = (! custom || ! custom->vars
+                          || ! mplist__assq (custom->vars, variable));
+         if (! plist)
+           {
+             if (no_custom)
+               return 0;
+             plist = mplist ();
+             mplist_add (config->vars, Mplist, plist);
+             M17N_OBJECT_UNREF (plist);
+             plist = mplist_add (plist, Msymbol, variable);
+           }
+         else
+           {
+             if (no_custom)
+               mplist__pop_unref (plist);
+             else
+               {
+                 plist = MPLIST_PLIST (plist); /* (NAME nil VALUE) */
+                 plist = MPLIST_NEXT (plist);  /* ([nil VALUE]) */
+                 mplist_set (plist, Mnil ,NULL);
+               }
+           }
+       }
+      else
+       {
+         if (plist)
+           {
+             plist = MPLIST_NEXT (MPLIST_PLIST (plist));
+             if (! MPLIST_TAIL_P (plist))
+               mplist_set (plist, Mnil, NULL);
+           }
+         else
+           {
+             plist = mplist ();
+             mplist_add (config->vars, Mplist, plist);
+             M17N_OBJECT_UNREF (plist);
+             plist = mplist_add (plist, Msymbol, variable);
+             plist = MPLIST_NEXT (plist);
+           }
+         mplist_add (plist, MPLIST_KEY (value), MPLIST_VAL (value));
+       }
+    }
+  config_all_variables (im_info);
+  im_info->tick = time (NULL);
+  return 0;
 }
 
 /*=*/
 
 /***en
-    @brief Destroy an input context.
+    @brief Get the name of per-user customization file.
+    
+    The minput_config_file () function returns the absolute path name
+    of per-user customization file into which minput_save_config ()
+    save configurations.  It is usually @c config.mic under the
+    directory <tt>${HOME}/.m17n.d</tt> (${HOME} is user's home
+    directory).  It is not assured that the file of the returned name
+    exists nor is readable/writable.  If minput_save_config () fails
+    and returns -1, an application program might check the file, make
+    it writable (if possible), and try minput_save_config () again.
 
-    The minput_destroy_ic () function destroys the input context $IC,
-    which must have been created by minput_create_ic ().  It calls
-    callback functions corresponding to #Minput_preedit_done,
-    #Minput_status_done, and #Minput_candidates_done in this order.  */
+    @return
+
+    This function returns a string.  As the string is kept in the
+    library, the caller must not modify nor free it.
 
+    @seealso
+    minput_save_config ()
+*/
 /***ja
-    @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤òÇ˲õ¤¹¤ë.
+    @brief ¥æ¡¼¥¶Ëè¤Î¥«¥¹¥¿¥Þ¥¤¥º¥Õ¥¡¥¤¥ë¤Î̾Á°¤òÆÀ¤ë.
+    
+    ´Ø¿ô minput_config_file () ¤Ï¡¢´Ø¿ô minput_save_config () ¤¬ÀßÄê¤ò
+    Êݸ¤¹¤ë¥æ¡¼¥¶Ëè¤Î¥«¥¹¥¿¥Þ¥¤¥º¥Õ¥¡¥¤¥ë¤Ø¤ÎÀäÂХѥ¹Ì¾¤òÊÖ¤¹¡£Ä̾ï¤Ï¡¢¥æ¡¼¥¶
+    ¤Î¥Û¡¼¥à¥Ç¥£¥ì¥¯¥È¥ê¤Î²¼¤Î¥Ç¥£¥ì¥¯¥È¥ê @c ".m17n.d" ¤Ë¤¢¤ë@c
+    "config.mic" ¤È¤Ê¤ë¡£ÊÖ¤µ¤ì¤¿Ì¾Á°¤Î¥Õ¥¡¥¤¥ë¤¬Â¸ºß¤¹¤ë¤«¡¢Æɤ߽ñ¤­¤Ç
+    ¤­¤ë¤«¤ÏÊݾڤµ¤ì¤Ê¤¤¡£´Ø¿ôminput_save_config () ¤¬¼ºÇÔ¤·¤Æ -1 ¤òÊÖ
+    ¤·¤¿¾ì¹ç¤Ë¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¥Õ¥¡¥¤¥ë¤Î¸ºß¤ò³Îǧ¤·¡¢
+    ¡Ê¤Ç¤­¤ì¤Ð¡Ë½ñ¤­¹þ¤ß²Äǽ¤Ë¤·ºÆÅÙminput_save_config () ¤ò»î¤¹¤³¤È¤¬
+    ¤Ç¤­¤ë¡£
 
-    ´Ø¿ô minput_destroy_ic () ¤Ï¡¢ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤òÇ˲õ¤¹¤ë¡£
-    ¤³¤ÎÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Ï minput_create_ic () 
-    ¤Ë¤è¤Ã¤Æºî¤é¤ì¤¿¤â¤Î¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£¤³¤Î´Ø¿ô¤Ï 
-    #Minput_preedit_done, #Minput_status_done, #Minput_candidates_done 
-    ¤ËÂбþ¤¹¤ë¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤ò¤³¤Î½ç¤Ë¸Æ¤Ö¡£
-  */
+    @return
 
-void
-minput_destroy_ic (MInputContext *ic)
+    ¤³¤Î´Ø¿ô¤Ïʸ»úÎó¤òÊÖ¤¹¡£Ê¸»úÎó¤Ï¥é¥¤¥Ö¥é¥ê¤¬´ÉÍý¤·¤Æ¤¤¤ë¤Î¤Ç¡¢¸Æ½Ð
+    Â¦¤¬½¤Àµ¤·¤¿¤ê²òÊü¤·¤¿¤ê¤¹¤ë¤³¤È¤Ï¤Ç¤­¤Ê¤¤¡£
+
+    @seealso
+    minput_save_config ()
+*/
+
+char *
+minput_config_file ()
 {
-  MDEBUG_PRINT2 ("  [IM] destroying context (%s %s) ... ",
-                msymbol_name (ic->im->name), msymbol_name (ic->im->language));
-  if (ic->im->driver.callback_list)
-    {
-      minput__callback (ic, Minput_preedit_done);
-      minput__callback (ic, Minput_status_done);
-      minput__callback (ic, Minput_candidates_done);
-    }
-  (*ic->im->driver.destroy_ic) (ic);
-  M17N_OBJECT_UNREF (ic->preedit);
-  M17N_OBJECT_UNREF (ic->produced);
-  M17N_OBJECT_UNREF (ic->plist);
-  MDEBUG_PRINT (" done\n");
-  free (ic);
+  MINPUT__INIT ();
+
+  return mdatabase__file (im_custom_mdb);
 }
 
 /*=*/
 
 /***en
-    @brief Filter an input key.
+    @brief Save configurations in per-user customization file.
 
-    The minput_filter () function filters input key $KEY according to
-    input context $IC, and calls callback functions corresponding to
-    #Minput_preedit_draw, #Minput_status_draw, and
-    #Minput_candidates_draw if the preedit text, the status, and the
-    current candidate are changed respectively.
+    The minput_save_config () function saves the configurations done
+    so far in the current session into the per-user customization
+    file.
 
-    To make the input method commit the current preedit text (if any)
-    and shift to the initial state, call this function with #Mnil as
-    $KEY.
+    @return
 
-    To inform the input method about the focus-out event, call this
-    function with #Minput_focus_out as $KEY.
+    If the operation was successful, 1 is returned.  If the per-user
+    customization file is currently locked, 0 is returned.  In that
+    case, the caller may wait for a while and try again.  If the
+    configuration file is not writable, -1 is returned.  In that case,
+    the caller may check the name of the file by calling
+    minput_config_file (), make it writable if possible, and try
+    again.
 
-    To inform the input method about the focus-in event, call this
-    function with #Minput_focus_in as $KEY.
+    @seealso
+    minput_config_file ()  */
+/***ja
+    @brief ÀßÄê¤ò¥æ¡¼¥¶Ëè¤Î¥«¥¹¥¿¥Þ¥¤¥º¥Õ¥¡¥¤¥ë¤ËÊݸ¤¹¤ë.
 
-    To inform the input method about the focus-move event (i.e. input
-    spot change within the same input context), call this function
-    with #Minput_focus_move as $KEY.
+    ´Ø¿ô minput_save_config () ¤Ï¸½¹Ô¤Î¥»¥Ã¥·¥ç¥ó¤Ç¤³¤ì¤Þ¤Ç¤Ë¹Ô¤Ã¤¿ÀßÄê
+    ¤ò¥æ¡¼¥¶Ëè¤Î¥«¥¹¥¿¥Þ¥¤¥º¥Õ¥¡¥¤¥ë¤ËÊݸ¤¹¤ë¡£
 
     @return
-    If $KEY is filtered out, this function returns 1.  In that case,
-    the caller should discard the key.  Otherwise, it returns 0, and
-    the caller should handle the key, for instance, by calling the
-    function minput_lookup () with the same key.  */
-
-/***ja
-    @brief ÆþÎÏ¥­¡¼¤ò¥Õ¥£¥ë¥¿¤¹¤ë.
 
-    ´Ø¿ô minput_filter () ¤ÏÆþÎÏ¥­¡¼ $KEY ¤òÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC 
-    ¤Ë±þ¤¸¤Æ¥Õ¥£¥ë¥¿¤·¡¢preedit ¥Æ¥­¥¹¥È¡¢¥¹¥Æ¡¼¥¿¥¹¡¢¸½»þÅÀ¤Ç¤Î¸õÊ䤬ÊѲ½¤·¤¿»þÅÀ¤Ç¡¢¤½¤ì¤¾¤ì
-    #Minput_preedit_draw, #Minput_status_draw,
-    #Minput_candidates_draw ¤ËÂбþ¤¹¤ë¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤ò¸Æ¤Ö¡£
-
-    @return 
-    $KEY ¤¬¥Õ¥£¥ë¥¿¤µ¤ì¤ì¤Ð¡¢¤³¤Î´Ø¿ô¤Ï 1 ¤òÊÖ¤¹¡£
-    ¤³¤Î¾ì¹ç¸Æ¤Ó½Ð¤·Â¦¤Ï¤³¤Î¥­¡¼¤ò¼Î¤Æ¤ë¤Ù¤­¤Ç¤¢¤ë¡£
-    ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð 0 ¤òÊÖ¤·¡¢¸Æ¤Ó½Ð¤·Â¦¤Ï¡¢¤¿¤È¤¨¤ÐƱ¤¸¥­¡¼¤Ç´Ø¿ô minput_lookup ()
-    ¤ò¸Æ¤Ö¤Ê¤É¤·¤Æ¡¢¤³¤Î¥­¡¼¤ò½èÍý¤¹¤ë¡£
+    À®¸ù¤¹¤ì¤Ð 1 ¤òÊÖ¤¹¡£¥æ¡¼¥¶Ëè¤Î¥«¥¹¥¿¥Þ¥¤¥º¥Õ¥¡¥¤¥ë¤¬¥í¥Ã¥¯¤µ¤ì¤Æ¤¤
+    ¤ì¤Ð 0 ¤òÊÖ¤¹¡£¤³¤Î¾ì¹ç¡¢¸Æ½Ð¦¤Ï¤·¤Ð¤é¤¯ÂԤäƺƻî¹Ô¤Ç¤­¤ë¡£ÀßÄê¥Õ¥¡
+    ¥¤¥ë¤¬½ñ¤­¹þ¤ßÉԲĤξì¹ç¡¢-1 ¤òÊÖ¤¹¡£¤³¤Î¾ì¹ç¡¢minput_config_file
+    () ¤ò¸Æ¤ó¤Ç¥Õ¥¡¥¤¥ë̾¤ò¥Á¥§¥Ã¥¯¤·¡¢¤Ç¤­¤ì¤Ð½ñ¤­¹þ¤ß²Äǽ¤Ë¤·¡¢ºÆ»î¹Ô
+    ¤Ç¤­¤ë¡£
 
-    @latexonly \IPAlabel{minput_filter} @endlatexonly
-*/
+    @seealso
+    minput_config_file ()  */
 
 int
-minput_filter (MInputContext *ic, MSymbol key, void *arg)
+minput_save_config (void)
 {
+  MPlist *data, *tail, *plist, *p, *elt;
   int ret;
 
-  if (! ic
-      || ! ic->active)
-    return 0;
-  ret = (*ic->im->driver.filter) (ic, key, arg);
+  MINPUT__INIT ();
+  ret = mdatabase__lock (im_custom_mdb);
+  if (ret <= 0)
+    return ret;
+  if (! im_config_list)
+    return 1;
+  update_custom_info ();
+  if (! im_custom_list)
+    im_custom_list = mplist ();
+
+  /* At first, reflect configuration in customization.  */
+  MPLIST_DO (plist, im_config_list)
+    {
+      MPlist *pl = MPLIST_PLIST (plist);
+      MSymbol language, name, extra, command, variable;
+      MInputMethodInfo *custom, *config;
 
-  if (ic->im->driver.callback_list)
+      language = MPLIST_SYMBOL (pl);
+      pl = MPLIST_NEXT (pl);
+      name = MPLIST_SYMBOL (pl);
+      pl = MPLIST_NEXT (pl);
+      extra = MPLIST_SYMBOL (pl);
+      pl = MPLIST_NEXT (pl);
+      config = MPLIST_VAL (pl);
+      custom = get_custom_info (config);
+      if (! custom)
+       custom = new_im_info (NULL, language, name, extra, im_custom_list);
+      if (config->cmds)
+       MPLIST_DO (pl, config->cmds)
+         {
+           elt = MPLIST_PLIST (pl);
+           command = MPLIST_SYMBOL (elt);
+           if (custom->cmds)
+             p = mplist__assq (custom->cmds, command);
+           else
+             custom->cmds = mplist (), p = NULL;
+           elt = MPLIST_NEXT (elt);
+           if (p)
+             {
+               p = MPLIST_NEXT (MPLIST_NEXT (MPLIST_PLIST (p)));
+               mplist_set (p, Mnil, NULL);
+             }
+           else
+             {
+               p = mplist ();
+               mplist_add (custom->cmds, Mplist, p);
+               M17N_OBJECT_UNREF (p);
+               mplist_add (p, Msymbol, command);
+               p = mplist_add (p, Msymbol, Mnil);
+               p = MPLIST_NEXT (p);
+             }
+           mplist__conc (p, elt);
+         }
+      if (config->vars)
+       MPLIST_DO (pl, config->vars)
+         {
+           elt = MPLIST_PLIST (pl);
+           variable = MPLIST_SYMBOL (elt);
+           if (custom->vars)
+             p = mplist__assq (custom->vars, variable);
+           else
+             custom->vars = mplist (), p = NULL;
+           elt = MPLIST_NEXT (elt);
+           if (p)
+             {
+               p = MPLIST_NEXT (MPLIST_NEXT (MPLIST_PLIST (p)));
+               mplist_set (p, Mnil, NULL);
+             }
+           else
+             {
+               p = mplist ();
+               mplist_add (custom->vars, Mplist, p);
+               M17N_OBJECT_UNREF (p);
+               mplist_add (p, Msymbol, variable);
+               p = mplist_add (p, Msymbol, Mnil);
+               p = MPLIST_NEXT (p);
+             }
+           mplist__conc (p, elt);
+         }
+    }
+  free_im_list (im_config_list);
+  im_config_list = NULL;
+
+  /* Next, reflect customization to the actual plist to be written.  */
+  data = tail = mplist ();
+  MPLIST_DO (plist, im_custom_list)
     {
-      if (ic->preedit_changed)
-       minput__callback (ic, Minput_preedit_draw);
-      if (ic->status_changed)
-       minput__callback (ic, Minput_status_draw);
-      if (ic->candidates_changed)
-       minput__callback (ic, Minput_candidates_draw);
+      MPlist *pl = MPLIST_PLIST (plist);
+      MSymbol language, name, extra;
+      MInputMethodInfo *custom, *im_info;
+
+      language = MPLIST_SYMBOL (pl);
+      pl  = MPLIST_NEXT (pl);
+      name = MPLIST_SYMBOL (pl);
+      pl = MPLIST_NEXT (pl);
+      extra = MPLIST_SYMBOL (pl);
+      pl = MPLIST_NEXT (pl);
+      custom = MPLIST_VAL (pl);
+      if ((! custom->cmds || MPLIST_TAIL_P (custom->cmds))
+         && (! custom->vars || MPLIST_TAIL_P (custom->vars)))
+       continue;
+      im_info = lookup_im_info (im_info_list, language, name, extra);
+      if (im_info)
+       {
+         if (im_info->cmds)
+           config_all_commands (im_info);
+         if (im_info->vars)
+           config_all_variables (im_info);
+       }
+      
+      elt = NULL;
+      if (custom->cmds && ! MPLIST_TAIL_P (custom->cmds))
+       {
+         MPLIST_DO (p, custom->cmds)
+           if (! MPLIST_TAIL_P (MPLIST_NEXT (MPLIST_PLIST (p))))
+             break;
+         if (! MPLIST_TAIL_P (p))
+           {
+             elt = mplist ();
+             pl = mplist ();
+             mplist_add (elt, Mplist, pl);
+             M17N_OBJECT_UNREF (pl);
+             pl = mplist_add (pl, Msymbol, Mcommand);
+             MPLIST_DO (p, custom->cmds)
+               if (! MPLIST_TAIL_P (MPLIST_NEXT (MPLIST_PLIST (p))))
+                 pl = mplist_add (pl, Mplist, MPLIST_PLIST (p));
+           }
+       }
+      if (custom->vars && ! MPLIST_TAIL_P (custom->vars))
+       {
+         MPLIST_DO (p, custom->vars)
+           if (! MPLIST_TAIL_P (MPLIST_NEXT (MPLIST_PLIST (p))))
+             break;
+         if (! MPLIST_TAIL_P (p))
+           {
+             if (! elt)
+               elt = mplist ();
+             pl = mplist ();
+             mplist_add (elt, Mplist, pl);
+             M17N_OBJECT_UNREF (pl);
+             pl = mplist_add (pl, Msymbol, Mvariable);
+             MPLIST_DO (p, custom->vars)
+               if (! MPLIST_TAIL_P (MPLIST_NEXT (MPLIST_PLIST (p))))
+                 pl = mplist_add (pl, Mplist, MPLIST_PLIST (p));
+           }
+       }
+      if (elt)
+       {
+         pl = mplist ();
+         mplist_push (elt, Mplist, pl);
+         M17N_OBJECT_UNREF (pl);
+         pl = mplist_add (pl, Msymbol, Minput_method);
+         pl = mplist_add (pl, Msymbol, language);
+         pl = mplist_add (pl, Msymbol, name);
+         if (extra != Mnil)
+           pl = mplist_add (pl, Msymbol, extra);
+         tail = mplist_add (tail, Mplist, elt);
+         M17N_OBJECT_UNREF (elt);
+       }
     }
 
-  return ret;
+  mplist_push (data, Mstring, ";; -*- mode:lisp; coding:utf-8 -*-");
+  ret = mdatabase__save (im_custom_mdb, data);
+  mdatabase__unlock (im_custom_mdb);
+  M17N_OBJECT_UNREF (data);
+  return (ret < 0 ? -1 : 1);
 }
 
-/*=*/
-
 /***en
-    @brief Look up a text produced in the input context.
-
-    The minput_lookup () function looks up a text in the input context
-    $IC.  $KEY must be identical to the one that was used in the previous call of
-    minput_filter ().
-
-    If a text was produced by the input method, it is concatenated
-    to M-text $MT.
+    @brief List available input methods.
 
-    This function calls #MInputDriver::lookup .
+    The minput_list () function returns a list of currently available
+    input methods whose language is $LANGUAGE.  If $LANGUAGE is #Mnil,
+    all input methods are listed.
 
     @return
-    If $KEY was correctly handled by the input method, this function
-    returns 0.  Otherwise, it returns -1, even though some text
-    might be produced in $MT.  */
-
-/***ja
-    @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥ÈÃæ¤Î¥Æ¥­¥¹¥È¤òõ¤¹.
+    The returned value is a plist of this form:
+       ((LANGUAGE-NAME INPUT-METHOD-NAME SANE) ...)
+    The third element SANE of each input method is #Mt if it can be
+    successfully used, or #Mnil if it has some problem (e.g. syntax
+    error of MIM file, unavailable external module, unavailable
+    including input method).  */
+
+#if EXAMPLE_CODE
+#include <stdio.h>
+#include <string.h>
+#include <m17n.h>
 
-    ´Ø¿ô minput_lookup () ¤ÏÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC Ãæ¤Î¥Æ¥­¥¹¥È¤òõ¤¹¡£
-    $KEY ¤Ï´Ø¿ô minput_filter () ¤Ø¤ÎľÁ°¤Î¸Æ¤Ó½Ð¤·¤ËÍѤ¤¤é¤ì¤¿¤â¤Î¤ÈƱ¤¸¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
+int
+main (int argc, char **argv)
+{
+  MPlist *imlist, *pl;
 
-    ¥Æ¥­¥¹¥È¤¬ÆþÎϥ᥽¥Ã¥É¤Ë¤è¤Ã¤ÆÀ¸À®¤µ¤ì¤Æ¤¤¤ì¤Ð¡¢¥Æ¥­¥¹¥È¤Ï M-text
-    $MT ¤ËÏ¢·ë¤µ¤ì¤ë¡£
+  M17N_INIT ();
+  imlist = minput_list ((argc > 1) ? msymbol (argv[1]) : Mnil);
+  for (pl = imlist; mplist_key (pl) != Mnil; pl = mplist_next (pl))
+    {
+      MPlist *p = mplist_value (pl);
+      MSymbol lang, name, sane;
 
-    ¤³¤Î´Ø¿ô¤Ï¡¢#MInputDriver::lookup ¤ò¸Æ¤Ö¡£
+      lang = mplist_value (p);
+      p = mplist_next (p);
+      name = mplist_value (p);
+      p = mplist_next (p);
+      sane = mplist_value (p);
 
-    @return 
-    $KEY ¤¬ÆþÎϥ᥽¥Ã¥É¤Ë¤è¤Ã¤ÆŬÀڤ˽èÍý¤Ç¤­¤ì¤Ð¡¢¤³¤Î´Ø¿ô¤Ï 0 ¤òÊÖ¤¹¡£
-    ¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤¹¡£
-    ¤³¤Î¾ì¹ç¤Ç¤â $MT ¤Ë²¿¤é¤«¤Î¥Æ¥­¥¹¥È¤¬À¸À®¤µ¤ì¤Æ¤¤¤ë¤³¤È¤¬¤¢¤ë¡£
+      printf ("%s %s %s\n", msymbol_name (lang), msymbol_name (name),
+             sane == Mt ? "ok" : "no");
+    }
 
-    @latexonly \IPAlabel{minput_lookup} @endlatexonly  */
+  m17n_object_unref (imlist);
+  M17N_FINI ();
+  exit (0);
+}
+#endif
 
-int
-minput_lookup (MInputContext *ic, MSymbol key, void *arg, MText *mt)
+MPlist *
+minput_list (MSymbol language)
 {
-  return (ic ? (*ic->im->driver.lookup) (ic, key, arg, mt) : -1);
+  MPlist *plist, *pl;
+  MPlist *imlist = mplist ();
+  
+  MINPUT__INIT ();
+  plist = mdatabase_list (Minput_method, language, Mnil, Mnil);
+  if (! plist)
+    return imlist;
+  MPLIST_DO (pl, plist)
+    {
+      MDatabase *mdb = MPLIST_VAL (pl);
+      MSymbol *tag = mdatabase_tag (mdb);
+      MPlist *imdata, *p, *elm;
+      int num_maps = 0, num_states = 0;
+
+      if (tag[2] == Mnil)
+       continue;
+      imdata = mdatabase_load (mdb);
+      if (! imdata)
+       continue;
+      MPLIST_DO (p, imdata)
+       if (MPLIST_PLIST_P (p))
+         {
+           /* Check these basic functionarity:
+              All external modules (if any) are loadable.
+              All included input method (if any) are loadable.
+              At least one map is defined or included.
+              At least one state is defined or included. */
+           MPlist *elt = MPLIST_PLIST (p);
+           MSymbol key;
+
+           if (MFAILP (MPLIST_SYMBOL_P (elt)))
+             break;
+           key = MPLIST_SYMBOL (elt);
+           if (key == Mmap)
+             num_maps++;
+           else if (key == Mstate)
+             num_states++;
+           else if (key == Mmodule)
+             {
+               MPLIST_DO (elt, MPLIST_NEXT (elt))
+                 {
+                   MIMExternalModule *external;
+
+                   if (MFAILP (MPLIST_PLIST_P (elt)))
+                     break;
+                   external = load_external_module (MPLIST_PLIST (elt));
+                   if (MFAILP (external))
+                     break;
+                   unload_external_module (external);
+                 }
+               if (! MPLIST_TAIL_P (elt))
+                 break;
+             }
+           else if (key == Minclude)
+             {
+               MInputMethodInfo *im_info;
+
+               elt = MPLIST_NEXT (elt);
+               if (MFAILP (MPLIST_PLIST_P (elt)))
+                 break;
+               im_info = get_im_info_by_tags (MPLIST_PLIST (elt));
+               if (MFAILP (im_info))
+                 break;
+               elt = MPLIST_NEXT (elt);
+               if (MFAILP (MPLIST_SYMBOL_P (elt)))
+                 break;
+               key = MPLIST_SYMBOL (elt);
+               if (key == Mmap)
+                 {
+                   if (! im_info->maps)
+                     break;
+                   num_maps++;
+                 }
+               else if (key == Mstate)
+                 {
+                   if (! im_info->states)
+                     break;
+                   num_states++;
+                 }
+             }
+         }
+      elm = mplist ();
+      mplist_add (elm, Msymbol, tag[1]);
+      mplist_add (elm, Msymbol, tag[2]);
+      if (MPLIST_TAIL_P (p) && num_maps > 0 && num_states > 0)
+       mplist_add (elm, Msymbol, Mt);
+      else
+       mplist_add (elm, Msymbol, Mnil);
+      mplist_push (imlist, Mplist, elm);
+      M17N_OBJECT_UNREF (elm);
+      M17N_OBJECT_UNREF (imdata);
+    }
+  M17N_OBJECT_UNREF (plist);
+  return imlist;
 }
+
+/*=*/
+/*** @} */
 /*=*/
+/***en
+    @name Obsolete functions
+*/
+/***ja
+    @name Obsolete ¤Ê´Ø¿ô
+*/
+/*** @{ */
 
+/*=*/
 /***en
-    @brief Set the spot of the input context.
+    @brief Get a list of variables of an input method (obsolete).
 
-    The minput_set_spot () function sets the spot of input context $IC
-    to coordinate ($X, $Y ) with the height specified by $ASCENT and $DESCENT .
-    The semantics of these values depends on the input method driver.
+    This function is obsolete.  Use minput_get_variable () instead.
 
-    For instance, a driver designed to work in a CUI environment may
-    use $X and $Y as the column- and row numbers, and may ignore $ASCENT and
-    $DESCENT .  A driver designed to work in a window system may
-    interpret $X and $Y as the pixel offsets relative to the origin of the
-    client window, and may interpret $ASCENT and $DESCENT as the ascent- and
-    descent pixels of the line at ($X . $Y ).
+    The minput_get_variables () function returns a plist (#MPlist) of
+    variables used to control the behavior of the input method
+    specified by $LANGUAGE and $NAME.  The plist is @e well-formed
+    (@ref m17nPlist) of the following format:
 
-    $FONTSIZE specifies the fontsize of preedit text in 1/10 point.
+@verbatim
+    (VARNAME (DOC-MTEXT DEFAULT-VALUE [ VALUE ... ] )
+     VARNAME (DOC-MTEXT DEFAULT-VALUE [ VALUE ... ] )
+     ...)
+@endverbatim
 
-    $MT and $POS are the M-text and the character position at the spot.
-    $MT may be @c NULL, in which case, the input method cannot get
-    information about the text around the spot.  */
+    @c VARNAME is a symbol representing the variable name.
 
-/***ja
-    @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤Î¥¹¥Ý¥Ã¥È¤òÀßÄꤹ¤ë.
+    @c DOC-MTEXT is an M-text describing the variable.
 
-    ´Ø¿ô minput_set_spot () ¤Ï¡¢ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC ¤Î¥¹¥Ý¥Ã¥È¤ò¡¢ºÂɸ ($X, $Y )
-    ¤Î°ÌÃ֤ˠ¡¢¹â¤µ $ASCENT¡¢ $DESCENT 
-    ¤ÇÀßÄꤹ¤ë¡£ ¤³¤ì¤é¤ÎÃͤΰÕÌ£¤ÏÆþÎϥ᥽¥Ã¥É¥É¥é¥¤¥Ð¤Ë°Í¸¤¹¤ë¡£
+    @c DEFAULT-VALUE is the default value of the variable.  It is a
+    symbol, integer, or M-text.
 
-    ¤¿¤È¤¨¤Ð CUI ´Ä¶­¤ÇÆ°ºî¤¹¤ë¥É¥é¥¤¥Ð¤Ï $X ¤È $Y 
-    ¤ò¤½¤ì¤¾¤ìÎó¤È¹Ô¤ÎÈÖ¹æ¤È¤·¤ÆÍѤ¤¡¢$ASCENT ¤È $DESCENT 
-    ¤ò̵»ë¤¹¤ë¤«¤â¤·¤ì¤Ê¤¤¡£ ¤Þ¤¿¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥àÍѤΥɥ饤¥Ð¤Ï
-    $X ¤È $Y ¤ò¥¯¥é¥¤¥¢¥ó¥È¥¦¥£¥ó¥É¥¦¤Î¸¶ÅÀ¤«¤é¤Î¥ª¥Õ¥»¥Ã¥È¤ò¥Ô¥¯¥»¥ëñ°Ì¤Çɽ¤·¤¿¤â¤Î¤È¤·¤Æ°·¤¤¡¢
-    $ASCENT ¤È $DESCENT ¤ò ($X . $Y )
-    ¤ÎÎó¤Î¥¢¥»¥ó¥È¤È¥Ç¥£¥»¥ó¥È¤ò¥Ô¥¯¥»¥ëñ°Ì¤Çɽ¤·¤¿¤â¤Î¤È¤·¤Æ°·¤¦¤«¤â¤·¤ì¤Ê¤¤¡£
+    @c VALUEs (if any) specifies the possible values of the variable.
+    If @c DEFAULT-VALUE is an integer, @c VALUE may be a plist (@c FROM
+    @c TO), where @c FROM and @c TO specifies a range of possible
+    values.
 
-    $FONTSIZE ¤Ë¤Ï preedit ¥Æ¥­¥¹¥È¤Î¥Õ¥©¥ó¥È¥µ¥¤¥º¤ò 1/10 ¥Ý¥¤¥ó¥Èñ°Ì¤Ç»ØÄꤹ¤ë¡£
+    For instance, suppose an input method has the variables:
 
-    $MT ¤È $POS ¤Ï¤½¤Î¥¹¥Ý¥Ã¥È¤Î M-text ¤Èʸ»ú°ÌÃ֤Ǥ¢¤ë¡£$MT ¤Ï @c
-    NULL ¤Ç¤â¤è¤¯¡¢¤½¤Î¾ì¹ç¤Ë¤ÏÆþÎϥ᥽¥Ã¥É¤Ï¥¹¥Ý¥Ã¥È¼þÊդΥƥ­¥¹¥È¤Ë´Ø¤¹¤ë¾ðÊó¤òÆÀ¤ë¤³¤È¤¬¤Ç¤­¤Ê¤¤¡£
-    */
+    @li name:intvar, description:"value is an integer",
+         initial value:0, value-range:0..3,10,20
 
-void
-minput_set_spot (MInputContext *ic, int x, int y,
-                int ascent, int descent, int fontsize,
-                MText *mt, int pos)
-{
-  ic->spot.x = x;
-  ic->spot.y = y;
-  ic->spot.ascent = ascent;
-  ic->spot.descent = descent;
-  ic->spot.fontsize = fontsize;
-  ic->spot.mt = mt;
-  ic->spot.pos = pos;
-  if (ic->im->driver.callback_list)
-    minput__callback (ic, Minput_set_spot);
-}
-/*=*/
+    @li name:symvar, description:"value is a symbol",
+         initial value:nil, value-range:a, b, c, nil
 
-/***en
-    @brief Toggle input method.
+    @li name:txtvar, description:"value is an M-text",
+         initial value:empty text, no value-range (i.e. any text)
 
-    The minput_toggle () function toggles the input method associated
-    with input context $IC.  */
-/***ja
-    @brief ÆþÎϥ᥽¥Ã¥É¤òÀÚÂؤ¨¤ë.
+    Then, the returned plist is as follows.
 
-    ´Ø¿ô minput_toggle () ¤ÏÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC 
-    ¤ËÂбþÉÕ¤±¤é¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤ò¥È¥°¥ë¤¹¤ë¡£
-    */
+@verbatim
+    (intvar ("value is an integer" 0 (0 3) 10 20)
+     symvar ("value is a symbol" nil a b c nil)
+     txtvar ("value is an M-text" ""))
+@endverbatim
 
-void
-minput_toggle (MInputContext *ic)
-{
-  if (ic->im->driver.callback_list)
-    minput__callback (ic, Minput_toggle);
-  ic->active = ! ic->active;
-}
+    @return
+    If the input method uses any variables, a pointer to #MPlist is
+    returned.  As the plist is kept in the library, the caller must not
+    modify nor free it.  If the input method does not use any
+    variable, @c NULL is returned.  */
+/***ja
+    @brief ÆþÎϥ᥽¥Ã¥É¤ÎÊÑ¿ô¥ê¥¹¥È¤òÆÀ¤ë.
 
-/*=*/
+    ´Ø¿ô minput_get_variables () ¤Ï¡¢$LANGUAGE ¤È $NAME ¤Ë¤è¤Ã¤Æ»ØÄꤵ
+    ¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤Î¿¶¤ëÉñ¤¤¤òÀ©¸æ¤¹¤ëÊÑ¿ô¤Î¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È
+    (#MPlist) ¤òÊÖ¤¹¡£¤³¤Î¥ê¥¹¥È¤Ï @e well-formed ¤Ç¤¢¤ê(@ref m17nPlist) °Ê
+    ²¼¤Î·Á¼°¤Ç¤¢¤ë¡£
 
-/***en
-    @brief Reset an input context.
+@verbatim
+    (VARNAME (DOC-MTEXT DEFAULT-VALUE [ VALUE ... ] )
+     VARNAME (DOC-MTEXT DEFAULT-VALUE [ VALUE ... ] )
+     ...)
+@endverbatim
 
-    The minput_reset_ic () function resets input context $IC by
-    calling a callback function corresponding to #Minput_reset.  It
-    resets the status of $IC to its initial one.  As the
-    current preedit text is deleted without commitment, if necessary,
-    call minput_filter () with the arg @r key #Mnil to force the input
-    method to commit the preedit in advance.  */
+    @c VARNAME ¤ÏÊÑ¿ô¤Î̾Á°¤ò¼¨¤¹¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£
 
-/***ja
-    @brief ÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È¤ò¥ê¥»¥Ã¥È¤¹¤ë.
+    @c DOC-MTEXT ¤ÏÊÑ¿ô¤òÀâÌÀ¤¹¤ë M-text ¤Ç¤¢¤ë¡£
 
-    ´Ø¿ô minput_reset_ic () ¤Ï #Minput_reset 
-    ¤ËÂбþ¤¹¤ë¥³¡¼¥ë¥Ð¥Ã¥¯´Ø¿ô¤ò¸Æ¤Ö¤³¤È¤Ë¤è¤Ã¤ÆÆþÎÏ¥³¥ó¥Æ¥¯¥¹¥È $IC 
-    ¤ò¥ê¥»¥Ã¥È¤¹¤ë¡£¥ê¥»¥Ã¥È¤È¤Ï¡¢¼ÂºÝ¤Ë¤ÏÆþÎϥ᥽¥Ã¥É¤ò½é´ü¾õÂ֤˰ܤ¹¤³¤È¤Ç¤¢¤ê¡¢¤·¤¿¤¬¤Ã¤Æ¡¢¤â¤·¸½ºßÆþÎÏÃæ¤Î¥Æ¥­¥¹¥È¤¬¤¢¤ì¤Ð¡¢¤½¤ì¤Ï¥³¥ß¥Ã¥È¤µ¤ì¤ë¡£
-    É¬Íפʤé¤Ð¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï minput_lookup () 
-    ¤òÆɤó¤Ç¤½¤Î¥³¥ß¥Ã¥È¤µ¤ì¤¿¥Æ¥­¥¹¥È¤ò¼è¤ê½Ð¤¹¤³¤È¤¬¤Ç¤­¡¢¤½¤ÎºÝ¡¢
-    minput_lookup () ¤Î°ú¿ô @c KEY ¤È @c ARG 
-    ¤Ï̵»ë¤µ¤ì¤ë¡£ */
-void
-minput_reset_ic (MInputContext *ic)
-{
-  if (ic->im->driver.callback_list)
-    minput__callback (ic, Minput_reset);
-}
+    @c DEFAULT-VALUE ¤ÏÊÑ¿ô¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤǤ¢¤ê¡¢¥·¥ó¥Ü¥ë¡¢À°¿ô¤â¤·¤¯¤Ï
+    M-text ¤Ç¤¢¤ë¡£
 
-/*=*/
+    @c VALUE ¤Ï¡¢¤â¤·»ØÄꤵ¤ì¤Æ¤¤¤ì¤ÐÊÑ¿ô¤Î¼è¤êÆÀ¤ëÃͤò¼¨¤¹¡£¤â¤·
+    @c DEFAULT-VALUE ¤¬À°¿ô¤Ê¤é¡¢ @c VALUE ¤Ï (@c FROM @c TO) ¤È¤¤¤¦·Á
+    ¤Î¥ê¥¹¥È¤Ç¤âÎɤ¤¡£¤³¤Î¾ì¹ç @c FROM ¤È @c TO ¤Ï²Äǽ¤ÊÃͤÎÈϰϤò¼¨¤¹¡£
 
-/***en
-    @brief Get title and icon filename of an input method.
+    Îã¤È¤·¤Æ¡¢¤¢¤ëÆþÎϥ᥽¥Ã¥É¤¬¼¡¤Î¤è¤¦¤ÊÊÑ¿ô¤ò»ý¤Ä¾ì¹ç¤ò¹Í¤¨¤è¤¦¡£
 
-    The minput_get_title_icon () function returns a plist containing a
-    title and icon filename (if any) of the input method specifies by
-    $LANGUAGE and $NAME.
+    @li name:intvar, ÀâÌÀ:"value is an integer",
+        ½é´üÃÍ:0, ÃͤÎÈÏ°Ï:0..3,10,20
 
-    The first element of the plist has key Mtext and the value is an
-    M-text of the title for identifying the input method.  The second
-    element (if any) has key M-text and the value is an M-text of the
-    icon image (absolute) filename for the same purpose.
+    @li name:symvar, ÀâÌÀ:"value is a symbol",
+         ½é´üÃÍ:nil, ÃͤÎÈÏ°Ï:a, b, c, nil
 
-    @return
-    If there exists the specified input method and it defines an
-    title, a plist is returned.  Otherwise, NULL is returned.  A caller
-    must free the plist by m17n_object_unref ().
-*/
+    @li name:txtvar, ÀâÌÀ:"value is an M-text",
+        ½é´üÃÍ:empty text, ÃͤÎÈϰϤʤ·(¤É¤ó¤Ê M-text ¤Ç¤â²Ä)
 
-MPlist *
-minput_get_title_icon (MSymbol language, MSymbol name)
-{
-  MPlist *plist = load_partial_im_info (language, name, Mnil, Mtitle);
-  MPlist *pl;
-  char *file = NULL;
-  MText *mt;
+    ¤³¤Î¾ì¹ç¡¢ÊÖ¤µ¤ì¤ë¥ê¥¹¥È¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë¡£
 
-  if (! plist)
-    return NULL;
-  if (MPLIST_TAIL_P (plist))
-    goto no_title;
-  pl = MPLIST_PLIST (plist);
-  pl = MPLIST_NEXT (pl);
-  if (! MPLIST_MTEXT_P (pl))
-    goto no_title;
-  M17N_OBJECT_REF (pl);
-  M17N_OBJECT_UNREF (plist);
-  plist = pl;
-  pl = MPLIST_NEXT (pl);  
-  if (MPLIST_MTEXT_P (pl))
-    {
-      if (mtext_nchars (MPLIST_MTEXT (pl)) > 0)
-       {
-         mt = MPLIST_MTEXT (pl);
-         file = mdatabase__find_file ((char *) MTEXT_DATA (mt));
-       }
-    }
-  else if (language != Mnil && name != Mnil)
+@verbatim
+    (intvar ("value is an integer" 0 (0 3) 10 20)
+     symvar ("value is a symbol" nil a b c nil)
+     txtvar ("value is an M-text" ""))
+@endverbatim
 
-    {
-      char *buf = alloca (MSYMBOL_NAMELEN (language) + MSYMBOL_NAMELEN (name)
-                         + 12);
+    @return 
+    ÆþÎϥ᥽¥Ã¥É¤¬²¿¤é¤«¤ÎÊÑ¿ô¤ò»ÈÍѤ·¤Æ¤¤¤ì¤Ð #MPlist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
+    ÊÖ¤µ¤ì¤ë¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È¤Ï¥é¥¤¥Ö¥é¥ê¤Ë¤è¤Ã¤Æ´ÉÍý¤µ¤ì¤Æ¤ª¤ê¡¢¸Æ¤Ó½Ð¤·Â¦¤ÇÊѹ¹¤·¤¿¤ê²òÊü¤·¤¿¤ê¤·¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
+    ÆþÎϥ᥽¥Ã¥É¤¬ÊÑ¿ô¤ò°ìÀÚ»ÈÍѤ·¤Æ¤Ê¤±¤ì¤Ð¡¢@c NULL ¤òÊÖ¤¹¡£  */
 
-      sprintf (buf, "icons/%s-%s.png", (char *) MSYMBOL_NAME (language), 
-              (char *) MSYMBOL_NAME (name));
-      file = mdatabase__find_file (buf);
-      if (! file && language == Mt)
-       {
-         sprintf (buf, "icons/%s.png", (char *) MSYMBOL_NAME (name));
-         file = mdatabase__find_file (buf);
-       }
-    }
+MPlist *
+minput_get_variables (MSymbol language, MSymbol name)
+{
+  MInputMethodInfo *im_info;
+  MPlist *vars;
 
-  if (file)
+  MINPUT__INIT ();
+
+  im_info = get_im_info (language, name, Mnil, Mvariable);
+  if (! im_info || ! im_info->configured_vars)
+    return NULL;
+
+  M17N_OBJECT_UNREF (im_info->bc_vars);
+  im_info->bc_vars = mplist ();
+  MPLIST_DO (vars, im_info->configured_vars)
     {
-      mt = mtext__from_data (file, strlen (file), MTEXT_FORMAT_UTF_8, 1);
-      free (file);
-      mplist_set (pl, Mtext, mt);
-      M17N_OBJECT_UNREF (mt);
+      MPlist *plist = MPLIST_PLIST (vars);
+      MPlist *elt = mplist ();
+
+      mplist_push (im_info->bc_vars, Mplist, elt);
+      mplist_add (elt, Msymbol, MPLIST_SYMBOL (plist));
+      elt = MPLIST_NEXT (elt);
+      mplist_set (elt, Mplist, mplist_copy (MPLIST_NEXT (plist)));
+      M17N_OBJECT_UNREF (elt);
     }
-  else
-    mplist_set (pl, Mnil, NULL);
-  return plist;
-
- no_title:
-  M17N_OBJECT_UNREF (plist);
-  return NULL;
+  return im_info->bc_vars;
 }
 
 /*=*/
 
 /***en
-    @brief Get description text of an input method.
+    @brief Set the initial value of an input method variable.
 
-    The minput_get_description () function returns an M-text that
-    describes the input method specified by $LANGUAGE and $NAME.
+    The minput_set_variable () function sets the initial value of
+    input method variable $VARIABLE to $VALUE for the input method
+    specified by $LANGUAGE and $NAME.
+
+    By default, the initial value is 0.
+
+    This setting gets effective in a newly opened input method.
 
     @return
-    If the specified input method has a description text, a pointer to
-    #MText is returned.  The caller has to free it by m17n_object_unref ().
-    If the input method does not have a description text, @c NULL is
-    returned.  */
+    If the operation was successful, 0 is returned.  Otherwise -1 is
+    returned, and #merror_code is set to @c MERROR_IM.  */
 /***ja
-    @brief ÆþÎϥ᥽¥Ã¥É¤ÎÀâÌÀ¥Æ¥­¥¹¥È¤òÆÀ¤ë.
+    @brief ÆþÎϥ᥽¥Ã¥ÉÊÑ¿ô¤Î½é´üÃͤòÀßÄꤹ¤ë.
 
-    ´Ø¿ô minput_get_description () ¤Ï¡¢$LANGUAGE ¤È $NAME ¤Ë¤è¤Ã¤Æ»ØÄê
-    ¤µ¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤òÀâÌÀ¤¹¤ë M-text ¤òÊÖ¤¹¡£
+    ´Ø¿ô minput_set_variable () ¤Ï¡¢$LANGUAGE ¤È $NAME 
+    ¤Ë¤è¤Ã¤Æ»ØÄꤵ¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤ÎÆþÎϥ᥽¥Ã¥ÉÊÑ¿ô $VARIABLE
+    ¤Î½é´üÃͤò¡¢ $VALUE ¤ËÀßÄꤹ¤ë¡£
 
-    @return »ØÄꤵ¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤¬ÀâÌÀ¤¹¤ë¥Æ¥­¥¹¥È¤ò»ý¤Ã¤Æ¤¤¤ì¤Ð¡¢
-    #MText ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¸Æ¤Ó½Ð¤·Â¦¤Ï¡¢¤½¤ì¤ò m17n_object_unref
-    () ¤òÍѤ¤¤Æ²òÊü¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ÆþÎϥ᥽¥Ã¥É¤ËÀâÌÀ¥Æ¥­¥¹¥È¤¬Ìµ¤±
-    ¤ì¤Ð@c NULL ¤òÊÖ¤¹¡£ */
+    ¥Ç¥Õ¥©¥ë¥È¤Î½é´üÃͤϠ0 ¤Ç¤¢¤ë¡£
 
-MText *
-minput_get_description (MSymbol language, MSymbol name)
+    ¤³¤ÎÀßÄê¤Ï¡¢¿·¤·¤¯¥ª¡¼¥×¥ó¤µ¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤«¤éÍ­¸ú¤È¤Ê¤ë¡£
+
+    @return
+    ½èÍý¤¬À®¸ù¤¹¤ì¤Ð 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢
+    #merror_code ¤ò @c MERROR_IM ¤ËÀßÄꤹ¤ë¡£  */
+
+int
+minput_set_variable (MSymbol language, MSymbol name,
+                    MSymbol variable, void *value)
 {
-  MPlist *plist = load_partial_im_info (language, name, Mnil, M_description);
-  MPlist *pl;
-  MText *mt = NULL;
+  MPlist *plist, *pl;
+  MInputMethodInfo *im_info;
+  int ret;
 
-  if (! plist)
-    return NULL;
-  if (MPLIST_TAIL_P (plist))
-    {
-      M17N_OBJECT_UNREF (plist);
-      return NULL;
-    }
-  pl = MPLIST_PLIST (plist);
-  pl = MPLIST_NEXT (pl);
-  if (MPLIST_MTEXT_P (pl))
+  MINPUT__INIT ();
+
+  if (variable == Mnil)
+    MERROR (MERROR_IM, -1);
+  plist = minput_get_variable (language, name, variable);
+  plist = MPLIST_PLIST (plist);
+  plist = MPLIST_NEXT (plist);
+  pl = mplist ();
+  mplist_add (pl, MPLIST_KEY (plist), value);
+  ret = minput_config_variable (language, name, variable, pl);
+  M17N_OBJECT_UNREF (pl);
+  if (ret == 0)
     {
-      mt = MPLIST_MTEXT (pl);
-      M17N_OBJECT_REF (mt);
+      im_info = get_im_info (language, name, Mnil, Mvariable);
+      im_info->tick = 0;
     }
-  M17N_OBJECT_UNREF (plist);
-  return mt;
+  return ret;
 }
 
+/*=*/
+
 /***en
     @brief Get information about input method commands.
 
@@ -4005,13 +6375,35 @@ minput_get_description (MSymbol language, MSymbol name)
 MPlist *
 minput_get_commands (MSymbol language, MSymbol name)
 {
-  MPlist *plist = get_nested_list (language, name, Mnil, M_command);
+  MInputMethodInfo *im_info;
+  MPlist *cmds;
 
-  return (MPLIST_TAIL_P (plist) ? NULL : plist);
+  MINPUT__INIT ();
+
+  im_info = get_im_info (language, name, Mnil, Mcommand);
+  if (! im_info || ! im_info->configured_vars)
+    return NULL;
+  M17N_OBJECT_UNREF (im_info->bc_cmds);
+  im_info->bc_cmds = mplist ();
+  MPLIST_DO (cmds, im_info->configured_cmds)
+    {
+      MPlist *plist = MPLIST_PLIST (cmds);
+      MPlist *elt = mplist ();
+
+      mplist_push (im_info->bc_cmds, Mplist, elt);
+      mplist_add (elt, MPLIST_SYMBOL (plist),
+                 mplist_copy (MPLIST_NEXT (plist)));
+      M17N_OBJECT_UNREF (elt);
+    }
+  return im_info->bc_cmds;
 }
 
+/*=*/
+
 /***en
-    @brief Assign a key sequence to an input method command.
+    @brief Assign a key sequence to an input method command (obsolete).
+
+    This function is obsolete.  Use minput_config_command () instead.
 
     The minput_assign_command_keys () function assigns input key
     sequence $KEYSEQ to input method command $COMMAND for the input
@@ -4029,7 +6421,7 @@ minput_get_commands (MSymbol language, MSymbol name)
 
     @return
     If the operation was successful, 0 is returned.  Otherwise -1 is
-    returned, and #merror_code is set to #MERROR_IM.  */
+    returned, and #merror_code is set to @c MERROR_IM.  */
 /***ja
     @brief ÆþÎϥ᥽¥Ã¥É¥³¥Þ¥ó¥É¤Ë¥­¡¼¥·¡¼¥¯¥¨¥ó¥¹¤ò³ä¤êÅö¤Æ¤ë.
 
@@ -4048,256 +6440,67 @@ minput_get_commands (MSymbol language, MSymbol name)
     ¤³¤Î³ä¤êÅö¤Æ¤Ï¡¢³ä¤êÅö¤Æ°Ê¹ß¿·¤·¤¯¥ª¡¼¥×¥ó¤µ¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤«¤éÍ­
     ¸ú¤Ë¤Ê¤ë¡£
 
-    @return ½èÍý¤¬À®¸ù¤¹¤ì¤Ð 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢
-    #merror_code ¤ò #MERROR_IM ¤ËÀßÄꤹ¤ë¡£  */
+    @return 
+    ½èÍý¤¬À®¸ù¤¹¤ì¤Ð 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢
+    #merror_code ¤ò @c MERROR_IM ¤ËÀßÄꤹ¤ë¡£  */
 
 int
 minput_assign_command_keys (MSymbol language, MSymbol name,
                            MSymbol command, MPlist *keyseq)
 {
-  MPlist *plist, *pl, *p;
+  int ret;
+
+  MINPUT__INIT ();
 
-  if (check_command_keyseq (keyseq) < 0
-      || ! (plist = get_nested_list (language, name, Mnil, M_command)))
+  if (command == Mnil)
     MERROR (MERROR_IM, -1);
-  pl = mplist_get (plist, command);
-  if (pl)
-    {
-      pl = MPLIST_NEXT (pl);
-      if (! keyseq)
-       while ((p = mplist_pop (pl)))
-         M17N_OBJECT_UNREF (p);
-      else
-       {
-         keyseq = mplist_copy (keyseq);
-         mplist_push (pl, Mplist, keyseq);
-         M17N_OBJECT_UNREF (keyseq);
-       }
-    }
-  else
+  if (keyseq)
     {
-      if (name == Mnil)
-       MERROR (MERROR_IM, -1);
-      if (! keyseq)
-       return 0;
-      /* Get global commands.  */
-      pl = get_nested_list (Mnil, Mnil, Mnil, M_command);
-      pl = mplist_get (pl, command);
-      if (! pl)
+      MPlist *plist;
+
+      if  (! check_command_keyseq (keyseq))
        MERROR (MERROR_IM, -1);
-      p = mplist ();
-      mplist_add (p, Mtext, mplist_value (pl));
-      keyseq = mplist_copy (keyseq);
-      mplist_add (p, Mplist, keyseq);
-      M17N_OBJECT_UNREF (keyseq);
-      mplist_push (plist, command, p);
-    }
-  return 0;
+      plist = mplist ();
+      mplist_add (plist, Mplist, keyseq);
+      keyseq = plist;
+    }  
+  else
+    keyseq = mplist ();
+  ret = minput_config_command (language, name, command, keyseq);
+  M17N_OBJECT_UNREF (keyseq);
+  return ret;
 }
 
-/***en
-    @brief Get a list of variables of an input method.
-
-    The minput_get_variables () function returns a plist (#MPlist) of
-    variables used to control the behavior of the input method
-    specified by $LANGUAGE and $NAME.  The plist is @e well-formed
-    (#m17nPlist) of the following format:
-
-@verbatim
-    (VARNAME (DOC-MTEXT DEFAULT-VALUE [ VALUE ... ] )
-     VARNAME (DOC-MTEXT DEFAULT-VALUE [ VALUE ... ] )
-     ...)
-@endverbatim
-
-    @c VARNAME is a symbol representing the variable name.
-
-    @c DOC-MTEXT is an M-text describing the variable.
-
-    @c DEFAULT-VALUE is the default value of the variable.  It is a
-    symbol, integer, or M-text.
-
-    @c VALUEs (if any) specifies the possible values of the variable.
-    If @c DEFAULT-VALUE is an integer, @c VALUE may be a plist (@c FROM
-    @c TO), where @c FROM and @c TO specifies a range of possible
-    values.
-
-    For instance, suppose an input method has the variables:
-
-    @li name:intvar, description:"value is an integer",
-         initial value:0, value-range:0..3,10,20
-
-    @li name:symvar, description:"value is a symbol",
-         initial value:nil, value-range:a, b, c, nil
-
-    @li name:txtvar, description:"value is an M-text",
-         initial value:empty text, no value-range (i.e. any text)
-
-    Then, the returned plist is as follows.
-
-@verbatim
-    (intvar ("value is an integer" 0 (0 3) 10 20)
-     symvar ("value is a symbol" nil a b c nil)
-     txtvar ("value is an M-text" ""))
-@endverbatim
-
-    @return
-    If the input method uses any variables, a pointer to #MPlist is
-    returned.  As the plist is kept in the library, a caller must not
-    modify nor free it.  If the input method does not use any
-    variable, @c NULL is returned.  */
-/***ja
-    @brief ÆþÎϥ᥽¥Ã¥É¤ÎÊÑ¿ô¥ê¥¹¥È¤òÆÀ¤ë.
-
-    ´Ø¿ô minput_get_variables () ¤Ï¡¢$LANGUAGE ¤È $NAME ¤Ë¤è¤Ã¤Æ»ØÄꤵ
-    ¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤Î¿¶¤ëÉñ¤¤¤òÀ©¸æ¤¹¤ëÊÑ¿ô¤Î¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È
-    (#MPlist) ¤òÊÖ¤¹¡£¤³¤Î¥ê¥¹¥È¤Ï @e well-formed ¤Ç¤¢¤ê(#m17nPlist) °Ê
-    ²¼¤Î·Á¼°¤Ç¤¢¤ë¡£
-
-@verbatim
-    (VARNAME (DOC-MTEXT DEFAULT-VALUE [ VALUE ... ] )
-     VARNAME (DOC-MTEXT DEFAULT-VALUE [ VALUE ... ] )
-     ...)
-@endverbatim
-
-    @c VARNAME ¤ÏÊÑ¿ô¤Î̾Á°¤ò¼¨¤¹¥·¥ó¥Ü¥ë¤Ç¤¢¤ë¡£
-
-    @c DOC-MTEXT ¤ÏÊÑ¿ô¤òÀâÌÀ¤¹¤ë M-text ¤Ç¤¢¤ë¡£
-
-    @c DEFAULT-VALUE ¤ÏÊÑ¿ô¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤǤ¢¤ê¡¢¥·¥ó¥Ü¥ë¡¢À°¿ô¤â¤·¤¯¤Ï
-    M-text ¤Ç¤¢¤ë¡£
-
-    @c VALUE ¤Ï¡¢¤â¤·»ØÄꤵ¤ì¤Æ¤¤¤ì¤ÐÊÑ¿ô¤Î¼è¤êÆÀ¤ëÃͤò¼¨¤¹¡£¤â¤·
-    @c DEFAULT-VALUE ¤¬À°¿ô¤Ê¤é¡¢ @c VALUE ¤Ï (@c FROM @c TO) ¤È¤¤¤¦·Á
-    ¤Î¥ê¥¹¥È¤Ç¤âÎɤ¤¡£¤³¤Î¾ì¹ç @c FROM ¤È @c TO ¤Ï²Äǽ¤ÊÃͤÎÈϰϤò¼¨¤¹¡£
-
-    Îã¤È¤·¤Æ¡¢¤¢¤ëÆþÎϥ᥽¥Ã¥É¤¬¼¡¤Î¤è¤¦¤ÊÊÑ¿ô¤ò»ý¤Ä¾ì¹ç¤ò¹Í¤¨¤è¤¦¡£
-
-    @li name:intvar, ÀâÌÀ:"value is an integer",
-        ½é´üÃÍ:0, ÃͤÎÈÏ°Ï:0..3,10,20
-
-    @li name:symvar, ÀâÌÀ:"value is a symbol",
-         ½é´üÃÍ:nil, ÃͤÎÈÏ°Ï:a, b, c, nil
-
-    @li name:txtvar, ÀâÌÀ:"value is an M-text",
-        ½é´üÃÍ:empty text, ÃͤÎÈϰϤʤ·(¤É¤ó¤Ê M-text ¤Ç¤â²Ä)
-
-    ¤³¤Î¾ì¹ç¡¢ÊÖ¤µ¤ì¤ë¥ê¥¹¥È¤Ï°Ê²¼¤Î¤è¤¦¤Ë¤Ê¤ë¡£
-
-@verbatim
-    (intvar ("value is an integer" 0 (0 3) 10 20)
-     symvar ("value is a symbol" nil a b c nil)
-     txtvar ("value is an M-text" ""))
-@endverbatim
-
-    @return 
-    ÆþÎϥ᥽¥Ã¥É¤¬²¿¤é¤«¤ÎÊÑ¿ô¤ò»ÈÍѤ·¤Æ¤¤¤ì¤Ð #MPlist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
-    ÊÖ¤µ¤ì¤ë¥×¥í¥Ñ¥Æ¥£¥ê¥¹¥È¤Ï¥é¥¤¥Ö¥é¥ê¤Ë¤è¤Ã¤Æ´ÉÍý¤µ¤ì¤Æ¤ª¤ê¡¢¸Æ¤Ó½Ð¤·Â¦¤ÇÊѹ¹¤·¤¿¤ê²òÊü¤·¤¿¤ê¤·¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
-    ÆþÎϥ᥽¥Ã¥É¤¬ÊÑ¿ô¤ò°ìÀÚ»ÈÍѤ·¤Æ¤Ê¤±¤ì¤Ð¡¢@c NULL ¤òÊÖ¤¹¡£  */
-
-MPlist *
-minput_get_variables (MSymbol language, MSymbol name)
-{
-  MPlist *plist = get_nested_list (language, name, Mnil, M_variable);
-
-  return (MPLIST_TAIL_P (plist) ? NULL : plist);
-}
+/*=*/
 
 /***en
-    @brief Set the initial value of an input method variable.
-
-    The minput_set_variable () function sets the initial value of
-    input method variable $VARIABLE to $VALUE for the input method
-    specified by $LANGUAGE and $NAME.
-
-    By default, the initial value is 0.
-
-    This setting gets effective in a newly opened input method.
-
-    @return
-    If the operation was successful, 0 is returned.  Otherwise -1 is
-    returned, and #merror_code is set to #MERROR_IM.  */
-/***ja
-    @brief ÆþÎϥ᥽¥Ã¥ÉÊÑ¿ô¤Î½é´üÃͤòÀßÄꤹ¤ë.
+    @brief Call a callback function
 
-    ´Ø¿ô minput_set_variable () ¤Ï¡¢$LANGUAGE ¤È $NAME 
-    ¤Ë¤è¤Ã¤Æ»ØÄꤵ¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤ÎÆþÎϥ᥽¥Ã¥ÉÊÑ¿ô $VARIABLE
-    ¤Î½é´üÃͤò¡¢ $VALUE ¤ËÀßÄꤹ¤ë¡£
-
-    ¥Ç¥Õ¥©¥ë¥È¤Î½é´üÃͤϠ0 ¤Ç¤¢¤ë¡£
-
-    ¤³¤ÎÀßÄê¤Ï¡¢¿·¤·¤¯¥ª¡¼¥×¥ó¤µ¤ì¤¿ÆþÎϥ᥽¥Ã¥É¤«¤éÍ­¸ú¤È¤Ê¤ë¡£
+    The minput_callback () functions calls a callback function
+    $COMMAND assigned for the input context $IC.  The caller must set
+    specific elements in $IC->plist if the callback function requires.
 
     @return
-    ½èÍý¤¬À®¸ù¤¹¤ì¤Ð 0 ¤òÊÖ¤¹¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð -1 ¤òÊÖ¤·¡¢
-    #merror_code ¤ò #MERROR_IM ¤ËÀßÄꤹ¤ë¡£  */
+    If there exists a specified callback function, 0 is returned.
+    Otherwise -1 is returned.  By side effects, $IC->plist may be
+    modified.  */
 
 int
-minput_set_variable (MSymbol language, MSymbol name,
-                    MSymbol variable, void *value)
+minput_callback (MInputContext *ic, MSymbol command)
 {
-  MPlist *plist, *val_element, *range_element;
-  MSymbol type;
-
-  plist = get_nested_list (language, name, Mnil, M_variable);
-  if (! plist)
-    MERROR (MERROR_IM, -1);
-  plist = mplist_find_by_value (plist, variable);
-  if (! plist)
-    MERROR (MERROR_IM, -1);
-  plist = MPLIST_PLIST (MPLIST_NEXT (plist));
-  val_element = MPLIST_NEXT (plist);
-  type = MPLIST_KEY (val_element);
-  range_element = MPLIST_NEXT (val_element);
-    
-  if (! MPLIST_TAIL_P (range_element))
-    {
-      if (type == Minteger)
-       {
-         int val = (int) value, this_val;
-      
-         MPLIST_DO (plist, range_element)
-           {
-             this_val = (int) MPLIST_VAL (plist);
-             if (MPLIST_PLIST_P (plist))
-               {
-                 int min_bound, max_bound;
-                 MPlist *pl = MPLIST_PLIST (plist);
-
-                 min_bound = (int) MPLIST_VAL (pl);
-                 pl = MPLIST_NEXT (pl);
-                 max_bound = (int) MPLIST_VAL (pl);
-                 if (val >= min_bound && val <= max_bound)
-                   break;
-               }
-             else if (val == this_val)
-               break;
-           }
-         if (MPLIST_TAIL_P (plist))
-           MERROR (MERROR_IM, -1);
-       }
-      else if (type == Msymbol)
-       {
-         MPLIST_DO (plist, range_element)
-           if (MPLIST_SYMBOL (plist) == (MSymbol) value)
-             break;
-         if (MPLIST_TAIL_P (plist))
-           MERROR (MERROR_IM, -1);
-       }
-      else                     /* type == Mtext */
-       {
-         MPLIST_DO (plist, range_element)
-           if (mtext_cmp (MPLIST_MTEXT (plist), (MText *) value) == 0)
-             break;
-         if (MPLIST_TAIL_P (plist))
-           MERROR (MERROR_IM, -1);
-         M17N_OBJECT_REF (value);
-       }
-    }
+  MInputCallbackFunc func;
 
-  mplist_set (val_element, type, value);
+  if (! ic->im->driver.callback_list)
+    return -1;
+  func = ((MInputCallbackFunc)
+         mplist_get_func (ic->im->driver.callback_list, command));
+  if (! func)
+    return -1;
+  (func) (ic, command);
   return 0;
 }
 
+/*** @} */ 
 /*** @} */
 /*=*/
 /*** @addtogroup m17nDebug */
@@ -4309,16 +6512,18 @@ minput_set_variable (MSymbol language, MSymbol name,
     @brief Dump an input method.
 
     The mdebug_dump_im () function prints the input method $IM in a
-    human readable way to the stderr.  $INDENT specifies how many
-    columns to indent the lines but the first one.
+    human readable way to the stderr or to what specified by the
+    environment variable MDEBUG_OUTPUT_FILE.  $INDENT specifies how
+    many columns to indent the lines but the first one.
 
     @return
     This function returns $IM.  */
 /***ja
     @brief ÆþÎϥ᥽¥Ã¥É¤ò¥À¥ó¥×¤¹¤ë.
 
-    ´Ø¿ô mdebug_dump_im () ¤ÏÆþÎϥ᥽¥Ã¥É $IM ¤ò stderr 
-    ¤Ë¿Í´Ö¤Ë²ÄÆɤʷÁ¤Ç°õºþ¤¹¤ë¡£$INDENT ¤Ï£²¹ÔÌܰʹߤΥ¤¥ó¥Ç¥ó¥È¤ò»ØÄꤹ¤ë¡£
+    ´Ø¿ô mdebug_dump_im () ¤ÏÆþÎϥ᥽¥Ã¥É $IM ¤òɸ½à¥¨¥é¡¼½ÐÎϤ⤷¤¯¤Ï
+    ´Ä¶­ÊÑ¿ô MDEBUG_DUMP_FONT ¤Ç»ØÄꤵ¤ì¤¿¥Õ¥¡¥¤¥ë¤Ë¿Í´Ö¤Ë²ÄÆɤʷÁ¤Ç½Ð
+    ÎϤ¹¤ë¡£$INDENT ¤Ï£²¹ÔÌܰʹߤΥ¤¥ó¥Ç¥ó¥È¤ò»ØÄꤹ¤ë¡£
 
     @return
     ¤³¤Î´Ø¿ô¤Ï $IM ¤òÊÖ¤¹¡£  */
@@ -4333,7 +6538,7 @@ mdebug_dump_im (MInputMethod *im, int indent)
   memset (prefix, 32, indent);
   prefix[indent] = '\0';
 
-  fprintf (stderr, "(input-method %s %s ", msymbol_name (im->language),
+  fprintf (mdebug__output, "(input-method %s %s ", msymbol_name (im->language),
           msymbol_name (im->name));
   mdebug_dump_mtext (im_info->title, 0, 0);
   if (im->name != Mnil)
@@ -4342,11 +6547,11 @@ mdebug_dump_im (MInputMethod *im, int indent)
 
       MPLIST_DO (state, im_info->states)
        {
-         fprintf (stderr, "\n%s  ", prefix);
+         fprintf (mdebug__output, "\n%s  ", prefix);
          dump_im_state (MPLIST_VAL (state), indent + 2);
        }
     }
-  fprintf (stderr, ")");
+  fprintf (mdebug__output, ")");
   return im;
 }