+static int
+update_custom_info (void)
+{
+ MPlist *plist, *pl;
+
+ if (im_custom_mdb)
+ {
+ if (mdatabase__check (im_custom_mdb) > 0)
+ return 1;
+ }
+ else
+ {
+ 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);
+ }
+
+ 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 ();
+
+ MPLIST_DO (pl, plist)
+ {
+ MSymbol language, name, extra;
+ MInputMethodInfo *im_info;
+ MPlist *im_data, *p;
+
+ 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);
+ if (language == Mnil || name == Mnil)
+ continue;
+ p = MPLIST_NEXT (p);
+ if (MPLIST_TAIL_P (p))
+ extra = Mnil;
+ else if (MPLIST_SYMBOL_P (p))
+ extra = MPLIST_SYMBOL (p);
+ else
+ 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;
+}
+
+static int
+update_global_info (void)
+{
+ MPlist *plist;
+
+ if (global_info)
+ {
+ int ret = mdatabase__check (global_info->mdb);
+
+ if (ret)
+ return ret;
+ fini_im_info (global_info);
+ }
+ else
+ {
+ MDatabase *mdb = mdatabase_find (Minput_method, Mt, Mnil, Mglobal);
+
+ 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;
+}
+
+
+/* Return an IM_INFO for the an 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)
+{
+ MPlist *plist;
+ MInputMethodInfo *im_info;
+ MDatabase *mdb;
+
+ 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
+ {
+ 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->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 int
+reload_im_info (MInputMethodInfo *im_info)
+{
+ int check;
+ MPlist *plist;
+
+ check = mdatabase__check (im_info->mdb);
+ if (check > 0)
+ return 0;
+ 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);
+ return 1;
+}
+
+static MInputMethodInfo *
+get_im_info_by_tags (MPlist *plist)
+{
+ MSymbol tag[3];
+ int i;
+
+ 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);
+}
+
+/* Check KEYSEQ, and return 1 if it is valid as a key sequence, return
+ 0 if not. */
+
+static int
+check_command_keyseq (MPlist *keyseq)
+{
+ if (MPLIST_PLIST_P (keyseq))
+ {
+ MPlist *p = MPLIST_PLIST (keyseq);
+
+ MPLIST_DO (p, p)
+ if (! MPLIST_SYMBOL_P (p) && ! MPLIST_INTEGER_P (p))
+ return 0;
+ return 1;
+ }
+ if (MPLIST_MTEXT_P (keyseq))
+ {
+ 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;
+}
+
+/* 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 *global_cmds = ((im_info->mdb && im_info != global_info)
+ ? global_info->cmds : NULL);
+ MPlist *tail;
+
+ im_info->cmds = tail = mplist ();
+
+ MPLIST_DO (plist, MPLIST_NEXT (plist))
+ {
+ /* PLIST ::= ((NAME DESC KEYSEQ ...) ...) */
+ MPlist *pl, *p, *global = NULL;
+ MSymbol name;
+
+ if (MFAILP (MPLIST_PLIST_P (plist)))
+ continue;
+ pl = MPLIST_PLIST (plist); /* PL ::= (NAME DESC KEYSEQ ...) */
+ if (MFAILP (MPLIST_SYMBOL_P (pl)))
+ continue;
+ name = MPLIST_SYMBOL (pl);
+ p = MPLIST_NEXT (pl); /* P ::= (DESC KEYSEQ ...) */
+ if (global_cmds
+ && (global = mplist__assq (global_cmds, name)))
+ {
+ /* GLOBAL ::= ((NAME DESC ...) ...) */
+ global = MPLIST_PLIST (global); /* (NAME DESC ...) */
+ global = MPLIST_NEXT (global); /* (DESC ...) */