*** empty log message ***
[m17n/m17n-lib.git] / src / database.c
index 4cf31c1..0f7891c 100644 (file)
 
    You should have received a copy of the GNU Lesser General Public
    License along with the m17n library; if not, write to the Free
 
    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
     @addtogroup m17nDatabase
     @brief The m17n database and API for it.
 
    02111-1307, USA.  */
 
 /***en
     @addtogroup m17nDatabase
     @brief The m17n database and API for it.
 
-    The m17n library dynamically acquires various kinds of information
-    in need from data in the <i> m17n database</i>.  Application
+    The m17n library acquires various kinds of information
+    from data in the <i> m17n database</i> on demand.  Application
     programs can also add/load their original data to/from the m17n
     programs can also add/load their original data to/from the m17n
-    database.  The m17n database contains multiple heterogeneous data,
-    and each data is identified by four tags; TAG0, TAG1, TAG2, TAG3.
-    Each tag must be a symbol.
+    database by setting the variable #mdatabase_dir to an
+    application-specific directory and storing data in it.  Users can
+    overwrite those data by storing preferable data in the directory
+    specified by the environment variable "M17NDIR", or if it is not
+    set, in the directory "~/.m17n.d".
+
+    The m17n database contains multiple heterogeneous data, and each
+    data is identified by four tags; TAG0, TAG1, TAG2, TAG3.  Each tag
+    must be a symbol.
 
     TAG0 specifies the type of data stored in the database as below.
 
     @li
 
     TAG0 specifies the type of data stored in the database as below.
 
     @li
-
     If TAG0 is #Mchar_table, the data is of the @e chartable @e
     type and provides information about each character.  In this case,
     TAG1 specifies the type of the information and must be #Msymbol,
     If TAG0 is #Mchar_table, the data is of the @e chartable @e
     type and provides information about each character.  In this case,
     TAG1 specifies the type of the information and must be #Msymbol,
     symbols.
 
     @li
     symbols.
 
     @li
-    
     If TAG0 is #Mcharset, the data is of the @e charset @e type
     and provides a decode/encode mapping table for a charset.  In this
     case, TAG1 must be a symbol representing a charset.  TAG2 and TAG3
     can be any symbols.
 
     @li 
     If TAG0 is #Mcharset, the data is of the @e charset @e type
     and provides a decode/encode mapping table for a charset.  In this
     case, TAG1 must be a symbol representing a charset.  TAG2 and TAG3
     can be any symbols.
 
     @li 
-    
     If TAG0 is neither #Mchar_table nor #Mcharset, the data is of
     If TAG0 is neither #Mchar_table nor #Mcharset, the data is of
-    the @e plist @e type.  See the documentation of the mdatabase_load
-    () function for the details.  In this case, TAG1, TAG2, and TAG3
-    can be any symbols.
+    the @e plist @e type.  See the documentation of the 
+    mdatabase_load () function for the details.  
+    In this case, TAG1, TAG2, and TAG3 can be any symbols.
 
     The notation \<TAG0, TAG1, TAG2, TAG3\> means a data with those
     tags.
 
     The notation \<TAG0, TAG1, TAG2, TAG3\> means a data with those
     tags.
     @addtogroup m17nDatabase
     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤È¤½¤ì¤Ë´Ø¤¹¤ë API.
 
     @addtogroup m17nDatabase
     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤È¤½¤ì¤Ë´Ø¤¹¤ë API.
 
-    m17n ¥é¥¤¥Ö¥é¥ê¤ÏɬÍפ˱þ¤¸¤ÆưŪ¤Ë @e m17n @e ¥Ç¡¼¥¿¥Ù¡¼¥¹ ¤«¤é¾ð
-    Êó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤âÆȼ«¤Î¥Ç¡¼¥¿¤ò 
-    m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£m17n 
-    ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤ÏÊ£¿ô¤Î¿Íͤʥǡ¼¥¿¤¬´Þ¤Þ¤ì¤Æ¤ª¤ê¡¢³Æ¥Ç¡¼¥¿¤Ï£´¤Ä¤Î
-    ¥¿¥° TAG0, TAG1, TAG2, TAG3¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£
+    m17n ¥é¥¤¥Ö¥é¥ê¤ÏɬÍפ˱þ¤¸¤ÆưŪ¤Ë @e m17n @e ¥Ç¡¼¥¿¥Ù¡¼¥¹ 
+    ¤«¤é¾ðÊó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤â¡¢Æȼ«¤Î¥Ç¡¼¥¿¤ò 
+    m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£
+    ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬Æȼ«¤Î¥Ç¡¼¥¿¤òÄɲᦼèÆÀ¤¹¤ë¤Ë¤Ï¡¢ÊÑ¿ô 
+    #mdatabase_dir ¤Ë¤½¤Î¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¥£¥ì¥¯¥È¥ê¤ò¥»¥Ã¥È¤·¡¢
+    ¤½¤ÎÃæ¤Ë¥Ç¡¼¥¿¤ò³ÊǼ¤¹¤ë¡£¥æ¡¼¥¶¤¬¤½¤Î¥Ç¡¼¥¿¤ò¥ª¡¼¥Ð¡¼¥é¥¤¥È¤·¤¿¤¤
+    ¤È¤­¤Ï¡¢´Ä¶­ÊÑ¿ô "M17NDIR" ¤Ç»ØÄꤵ¤ì¤ë¥Ç¥£¥ì¥¯¥È¥ê¡Ê»ØÄꤵ¤ì¤Æ¤¤¤Ê
+    ¤¤¤È¤­¤Ï "~/.m17n.d" ¤È¤¤¤¦¥Ç¥£¥ì¥¯¥È¥ê¡Ë¤ËÊ̤Υǡ¼¥¿¤òÃÖ¤¯¡£
 
 
-    TAG0 ¤Ï¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤ò°Ê²¼¤Î¤è¤¦¤Ë»ØÄꤹ¤ë¡£
+    m17n 
+    ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤ÏÊ£¿ô¤Î¿Íͤʥǡ¼¥¿¤¬´Þ¤Þ¤ì¤Æ¤ª¤ê¡¢³Æ¥Ç¡¼¥¿¤Ï
+    TAG0, TAG1, TAG2, TAG3¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Î£´¤Ä¤Î¥¿¥°¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£
 
 
-    @li 
-
-    TAG0 ¤¬ #Mchar_table ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e chartable¥¿¥¤¥× ¤È¸Æ¤Ð¤ì¡¢
-    ³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë
-    ¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Msymbol, #Minteger, #Mstring, #Mtext, #Mplist ¤Î
-    ¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
+    TAG0 ¤Ë¤è¤Ã¤Æ¡¢¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ï¼¡¤Î¤è¤¦¤Ë»ØÄꤵ¤ì¤ë¡£
 
     @li 
 
     @li 
+    TAG0 ¤¬ #Mchar_table ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e chartable¥¿¥¤¥× 
+    ¤È¸Æ¤Ð¤ì¡¢³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç
+    TAG1 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Msymbol, #Minteger, #Mstring,
+    #Mtext, #Mplist ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
 
 
-    TAG0 ¤¬ #Mcharset ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e charset¥¿¥¤¥× ¤È¸Æ¤Ð¤ì¡¢Ê¸
-    »ú¥»¥Ã¥ÈÍѤΥǥ³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥Þ¥Ã¥×¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1 ¤Ï
-    Ê¸»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó
-    ¥Ü¥ë¤Ç¤è¤¤¡£
+    @li 
+    TAG0 ¤¬ #Mcharset ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e charset¥¿¥¤¥× 
+    ¤È¸Æ¤Ð¤ì¡¢Ê¸»ú¥»¥Ã¥ÈÍѤΥǥ³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥Þ¥Ã¥×¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1
+    ¤Ïʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£TAG2 ¤È TAG3
+    ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
 
     @li
 
     @li
-
     TAG0 ¤¬ #Mchar_table ¤Ç¤â #Mcharset ¤Ç¤â¤Ê¤¤¾ì¹ç¡¢¤½¤Î¥Ç¡¼¥¿¤Ï @e
     TAG0 ¤¬ #Mchar_table ¤Ç¤â #Mcharset ¤Ç¤â¤Ê¤¤¾ì¹ç¡¢¤½¤Î¥Ç¡¼¥¿¤Ï @e
-    plist ¥¿¥¤¥× ¤Ç¤¢¤ë¡£¾ÜºÙ¤Ë´Ø¤·¤Æ¤Ï´Ø¿ô mdatabase_load () ¤ÎÀâÌÀ¤ò
-    »²¾È¤Î¤³¤È¡£¤³¤Î¾ì¹ç TAG1¡¢TAG2¡¢TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
+    plist¥¿¥¤¥× ¤Ç¤¢¤ë¡£¾ÜºÙ¤Ë´Ø¤·¤Æ¤Ï´Ø¿ô mdatabase_load () 
+    ¤ÎÀâÌÀ¤ò»²¾È¤Î¤³¤È¡£¤³¤Î¾ì¹ç TAG1¡¢TAG2¡¢TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
 
 
-    ÆÃÄê¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò \<TAG0, TAG1, TAG2, TAG3\> ¤È¤¤¤¦·Á
-    ¼°¤Çɽ¤ï¤¹¡£
+    ÆÃÄê¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò \<TAG0, TAG1, TAG2, TAG3\> 
+    ¤È¤¤¤¦·Á¼°¤Çɽ¤¹¡£
 
 
-    ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢¤Þ¤º´Ø¿ô mdatabase_find () ¤ò»È¤Ã¤Æ¥Ç¡¼
-    ¥¿¥Ù¡¼¥¹¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÝ»ý¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¡Ê#MDatabase ·¿¡Ë¤Ø¤Î
-    ¥Ý¥¤¥ó¥¿¤òÆÀ¤ë¡£¤½¤ì¤ËÀ®¸ù¤·¤¿¤é¡¢ mdatabase_load () ¤Ë¤è¤Ã¤Æ¼ÂºÝ
-    ¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤¹¤ë¡£¹½Â¤ÂΠ#MDatabase ¼«¿È¤¬¤É¤¦¼ÂÁõ¤µ¤ì
-    ¤Æ¤¤¤ë¤«¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£
+    ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢¤Þ¤º´Ø¿ô mdatabase_find () 
+    ¤ò»È¤Ã¤Æ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÝ»ý¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¡Ê#MDatabase
+    ·¿¡Ë¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÆÀ¤ë¡£¤½¤ì¤ËÀ®¸ù¤·¤¿¤é¡¢ mdatabase_load () 
+    ¤Ë¤è¤Ã¤Æ¼ÂºÝ¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤¹¤ë¡£¹½Â¤ÂΠ#MDatabase 
+    ¼«¿È¤¬¤É¤¦¼ÂÁõ¤µ¤ì¤Æ¤¤¤ë¤«¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£
 
     @latexonly \IPAlabel{database} @endlatexonly
 */
 
     @latexonly \IPAlabel{database} @endlatexonly
 */
 #include <sys/stat.h>
 #include <unistd.h>
 #include <limits.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <limits.h>
+#include <glob.h>
+#include <time.h>
+#include <libgen.h>
 
 #include "m17n.h"
 #include "m17n-misc.h"
 
 #include "m17n.h"
 #include "m17n-misc.h"
 
 /** The file containing a list of databases.  */
 #define MDB_DIR "mdb.dir"
 
 /** The file containing a list of databases.  */
 #define MDB_DIR "mdb.dir"
-#define MDB_DIR_LEN 8
+/** Length of MDB_DIR.  */
+#define MDB_DIR_LEN 7
 
 
-/** List of database directories.  */ 
-static MPlist *mdb_dir_list;
+#define MAX_TIME(TIME1, TIME2) ((TIME1) >= (TIME2) ? (TIME1) : (TIME2))
+
+#define GEN_PATH(path, dir, dir_len, file, file_len)   \
+  (dir_len + file_len > PATH_MAX ? 0                   \
+   : (memcpy (path, dir, dir_len),                     \
+      memcpy (path + dir_len, file, file_len),         \
+      path[dir_len + file_len] = '\0', 1))
+
+static MSymbol Masterisk;
 
 /** Structure for a data in the m17n database.  */
 
 
 /** Structure for a data in the m17n database.  */
 
@@ -161,15 +179,7 @@ struct MDatabase
   void *extra_info;
 };
 
   void *extra_info;
 };
 
-/** List of all data.  */
-struct MDatabaseList
-{
-  int size, inc, used;
-  MDatabase *mdbs;
-};
-
-static struct MDatabaseList mdb_list;
-
+static MPlist *mdatabase__list;
 
 static int
 read_number (char *buf, int *i)
 
 static int
 read_number (char *buf, int *i)
@@ -255,13 +265,13 @@ load_chartable (FILE *fp, MSymbol type)
        i++, to = read_number (buf, &i);
       else
        to = from;
        i++, to = read_number (buf, &i);
       else
        to = from;
-      if (from < 0 || to < 0)
-       goto label_error;
+      if (from < 0 || to < from)
+       continue;
 
       while (buf[i] && isspace ((unsigned) buf[i])) i++;
       c = buf[i];
       if (!c)
 
       while (buf[i] && isspace ((unsigned) buf[i])) i++;
       c = buf[i];
       if (!c)
-       break;
+       continue;
 
       if (type == Mstring)
        {
 
       if (type == Mstring)
        {
@@ -300,6 +310,18 @@ load_chartable (FILE *fp, MSymbol type)
        }
       else if (type == Msymbol)
        {
        }
       else if (type == Msymbol)
        {
+         char *p = buf + i;
+
+         while (*p && ! isspace (*p)) 
+           {
+             if (*p == '\\' && p[1] != '\0')
+               {
+                 memmove (p, p + 1, buf + len - (p + 1));
+                 len--;
+               }
+             p++;
+           }
+         *p = '\0';
          if (! strcmp (buf + i, "nil"))
            val = (void *) Mnil;
          else
          if (! strcmp (buf + i, "nil"))
            val = (void *) Mnil;
          else
@@ -412,183 +434,800 @@ gen_database_name (char *buf, MSymbol *tags)
   strcpy (buf, msymbol_name (tags[0]));
   for (i = 1; i < 4; i++)
     {
   strcpy (buf, msymbol_name (tags[0]));
   for (i = 1; i < 4; i++)
     {
-      strcat (buf, ", ");
+      strcat (buf, ",");
       strcat (buf, msymbol_name (tags[i]));
     }
   return buf;
 }
 
       strcat (buf, msymbol_name (tags[i]));
     }
   return buf;
 }
 
+char *
+find_file (MDatabaseInfo *db_info, struct stat *buf)
+{
+  MPlist *plist;
+  char path[PATH_MAX + 1];
+
+  MPLIST_DO (plist, mdatabase__dir_list)
+    {
+      MDatabaseInfo *dir_info = MPLIST_VAL (plist);
+
+      if (dir_info->status != MDB_STATUS_DISABLED
+         && GEN_PATH (path, dir_info->filename, dir_info->len,
+                      db_info->filename, db_info->len)
+         && stat (path, buf) == 0)
+       return strdup (path);
+    }
+  return NULL;
+}
+
+
+/* Return the absolute file name for DB_INFO->filename.  If BUF is
+   non-NULL, store the result of `stat' call in it.  It returns NULL
+   if no absolute file name was found.  */
+
+char *
+get_database_file (MDatabaseInfo *db_info, struct stat *buf)
+{
+  if (db_info->status == MDB_STATUS_DISABLED)
+    return NULL;
+  if (db_info->absolute_filename)
+    {
+      if (buf)
+       stat (db_info->absolute_filename, buf);
+    }
+  else
+    {
+      struct stat stat_buf;
+      struct stat *statbuf = buf ? buf : &stat_buf;
+
+      db_info->absolute_filename = find_file (db_info, statbuf);
+    }
+
+  return db_info->absolute_filename;
+}
+
 static void *
 load_database (MSymbol *tags, void *extra_info)
 {
 static void *
 load_database (MSymbol *tags, void *extra_info)
 {
-  FILE *fp;
-  char *filename = (char *) extra_info;
+  MDatabaseInfo *db_info = extra_info;
   void *value;
   void *value;
+  char *filename = get_database_file (db_info, NULL);
+  FILE *fp;
+  int mdebug_mask = MDEBUG_DATABASE;
+  char buf[256];
 
 
-  if (filename[0] == '/')
-    fp = fopen (filename, "r");
-  else
+  MDEBUG_PRINT1 (" [DB] <%s>", gen_database_name (buf, tags));
+  if (! filename || ! (fp = fopen (filename, "r")))
     {
     {
-      MPlist *plist;
-      char path[PATH_MAX];
-
-      MPLIST_DO (plist, mdb_dir_list)
-       {
-         strcpy (path, (char *) MPLIST_VAL (plist));
-         strcat (path, filename);
-         fp = fopen (path, "r");
-         if (fp)
-           break;
-       }
+      if (filename)
+       MDEBUG_PRINT1 (" open fail: %s\n", filename);
+      else
+       MDEBUG_PRINT1 (" not found: %s\n", db_info->filename);
+      MERROR (MERROR_DB, NULL);
     }
     }
-  if (! fp)
-    MERROR (MERROR_DB, NULL);
+
+  MDEBUG_PRINT1 (" from %s\n", filename);
 
   if (tags[0] == Mchar_table)
     value = load_chartable (fp, tags[1]);
   else if (tags[0] == Mcharset)
     value = load_charset (fp, tags[1]);
   else
 
   if (tags[0] == Mchar_table)
     value = load_chartable (fp, tags[1]);
   else if (tags[0] == Mcharset)
     value = load_charset (fp, tags[1]);
   else
-    value = mplist__from_file (fp);
+    value = mplist__from_file (fp, NULL);
   fclose (fp);
 
   if (! value)
     MERROR (MERROR_DB, NULL);
   fclose (fp);
 
   if (! value)
     MERROR (MERROR_DB, NULL);
+  db_info->time = time (NULL);
   return value;
 }
 
 
   return value;
 }
 
 
-/** Copy DIRNAME to a newly allocated memory and return it.  If
-    DIRNAME does not end with a slash, append a slash to the new memory.  */
+/** Return a newly allocated MDatabaseInfo for DIRNAME.  */
 
 
-static char *
-duplicate_dirname (char *dirname)
+static MDatabaseInfo *
+get_dir_info (char *dirname)
 {
 {
-  struct stat buf;
-  int len;
-  char *str;
+  MDatabaseInfo *dir_info;
+
+  MSTRUCT_CALLOC (dir_info, MERROR_DB);
+  if (dirname)
+    {
+      int len = strlen (dirname);
+
+      if (len + MDB_DIR_LEN < PATH_MAX)
+       {
+         MTABLE_MALLOC (dir_info->filename, len + 2, MERROR_DB);
+         memcpy (dir_info->filename, dirname, len + 1);
+         /* Append PATH_SEPARATOR if DIRNAME doesn't end with it.  */
+         if (dir_info->filename[len - 1] != PATH_SEPARATOR)
+           {
+             dir_info->filename[len] = PATH_SEPARATOR;
+             dir_info->filename[++len] = '\0';
+           }
+         dir_info->len = len;
+         dir_info->status = MDB_STATUS_OUTDATED;
+       }
+      else
+       dir_info->status = MDB_STATUS_DISABLED; 
+    }
+  else
+    dir_info->status = MDB_STATUS_DISABLED;
+  return dir_info;
+}
 
 
-  if (! dirname
-      || stat (dirname, &buf) < 0)
+static MDatabase *
+find_database (MSymbol tags[4])
+{
+  MPlist *plist;
+  int i;
+  
+  if (! mdatabase__list)
     return NULL;
     return NULL;
+  for (i = 0, plist = mdatabase__list; i < 4; i++)
+    {
+      plist = mplist__assq (plist, tags[i]);
+      if (! plist)
+       return NULL;
+      plist = MPLIST_PLIST (plist);
+      plist = MPLIST_NEXT (plist);
+    }
+  return MPLIST_VAL (plist);
+}
+
+static void
+free_db_info (MDatabaseInfo *db_info)
+{
+  free (db_info->filename);
+  if (db_info->absolute_filename
+      && db_info->filename != db_info->absolute_filename)
+    free (db_info->absolute_filename);
+  free (db_info);
+}
+
+static int
+check_version (MText *version)
+{
+  char *verstr = (char *) MTEXT_DATA (version);
+  char *endp = verstr + mtext_nbytes (version);
+  int ver[3];
+  int i;
 
 
-  len = strlen (dirname);
-  MTABLE_MALLOC (str, len + 2, MERROR_DB);
-  memcpy (str, dirname, len + 1);
-  if (str[len - 1] != '/')
+  ver[0] = ver[1] = ver[2] = 0;
+  for (i = 0; verstr < endp; verstr++)
     {
     {
-      str[len] = '/';
-      str[len + 1] = '\0';
+      if (*verstr == '.')
+       {
+         i++;
+         if (i == 3)
+           break;
+         continue;
+       }
+      if (! isdigit (*verstr))
+       break;
+      ver[i] = ver[i] * 10 + (*verstr - '0');
     }
     }
-  return str;
+  return (ver[0] < M17NLIB_MAJOR_VERSION
+         || (ver[0] == M17NLIB_MAJOR_VERSION
+             && (ver[1] < M17NLIB_MINOR_VERSION
+                 || (ver[1] == M17NLIB_MINOR_VERSION
+                     && ver[2] <= M17NLIB_PATCH_LEVEL))));
+}
+
+static MDatabase *
+register_database (MSymbol tags[4],
+                  void *(*loader) (MSymbol *, void *),
+                  void *extra_info, enum MDatabaseStatus status)
+{
+  MDatabase *mdb;
+  MDatabaseInfo *db_info;
+  int i;
+  MPlist *plist;
+
+  if (! mdatabase__list)
+    mdatabase__list = mplist ();
+
+  for (i = 0, plist = mdatabase__list; i < 4; i++)
+    {
+      MPlist *pl = mplist__assq (plist, tags[i]);
+
+      if (pl)
+       pl = MPLIST_PLIST (pl);
+      else
+       {
+         pl = mplist ();
+         mplist_add (pl, Msymbol, tags[i]);
+         mplist_push (plist, Mplist, pl);
+         M17N_OBJECT_UNREF (pl);
+       }
+      plist = MPLIST_NEXT (pl);
+    }
+
+  if (MPLIST_TAIL_P (plist))
+    {
+      MSTRUCT_MALLOC (mdb, MERROR_DB);
+      for (i = 0; i < 4; i++)
+       mdb->tag[i] = tags[i];
+      mdb->loader = loader;
+      if (loader == load_database)
+       {
+         MSTRUCT_CALLOC (db_info, MERROR_DB);
+         mdb->extra_info = db_info;
+       }
+      else
+       {
+         db_info = NULL;
+         mdb->extra_info = extra_info;
+       }
+      mplist_push (plist, Mt, mdb);
+    }
+  else
+    {
+      mdb = MPLIST_VAL (plist);
+      if (loader == load_database)
+       db_info = mdb->extra_info;
+      else
+       db_info = NULL;
+    }
+
+  if (db_info)
+    {
+      db_info->status = status;
+      if (! db_info->filename
+         || strcmp (db_info->filename, (char *) extra_info) != 0)
+       {
+         if (db_info->filename)
+           free (db_info->filename);
+         if (db_info->absolute_filename
+             && db_info->filename != db_info->absolute_filename)
+           free (db_info->absolute_filename);
+         db_info->filename = strdup ((char *) extra_info);
+         db_info->len = strlen ((char *) extra_info);
+         db_info->time = 0;
+       }
+      if (db_info->filename[0] == PATH_SEPARATOR)
+       db_info->absolute_filename = db_info->filename;
+      else
+       db_info->absolute_filename = NULL;
+    }
+
+  if (mdb->tag[0] == Mchar_table
+      && mdb->tag[2] != Mnil
+      && (mdb->tag[1] == Mstring || mdb->tag[1] == Mtext
+         || mdb->tag[1] == Msymbol || mdb->tag[1] == Minteger
+         || mdb->tag[1] == Mplist))
+    mchar__define_prop (mdb->tag[2], mdb->tag[1], mdb);
+  return mdb;
+}
+
+static void
+register_databases_in_files (MSymbol tags[4], glob_t *globbuf, int headlen)
+{
+  int i, j;
+  MPlist *load_key = mplist ();
+  FILE *fp;
+  MPlist *plist;
+
+  for (i = 0; i < globbuf->gl_pathc; i++)
+    {
+      if (! (fp = fopen (globbuf->gl_pathv[i], "r")))
+       continue;
+      plist = mplist__from_file (fp, load_key);
+      fclose (fp);
+      if (! plist)
+       continue;
+      if (MPLIST_PLIST_P (plist))
+       {
+         MPlist *pl;
+         MSymbol tags2[4];
+
+         for (j = 0, pl = MPLIST_PLIST (plist); j < 4 && MPLIST_SYMBOL_P (pl);
+              j++, pl = MPLIST_NEXT (pl))
+           tags2[j] = MPLIST_SYMBOL (pl);
+         for (; j < 4; j++)
+           tags2[j] = Mnil;
+         for (j = 0; j < 4; j++)
+           if (tags[j] == Masterisk ? tags2[j] == Mnil
+               : (tags[j] != Mnil && tags[j] != tags2[j]))
+             break;
+         if (j == 4)
+           {
+             MText *version = NULL;
+
+             MPLIST_DO (pl, pl)
+               version = MPLIST_MTEXT_P (pl) ? MPLIST_MTEXT (pl) : NULL;
+             if (! version || check_version (version))
+               register_database (tags2, load_database,
+                                  globbuf->gl_pathv[i] + headlen,
+                                  MDB_STATUS_AUTO);
+           }
+       }
+      M17N_OBJECT_UNREF (plist);
+    }
+  M17N_OBJECT_UNREF (load_key);
 }
 
 \f
 /* Internal API */
 
 }
 
 \f
 /* Internal API */
 
+/** List of database directories.  */ 
+MPlist *mdatabase__dir_list;
+
 int
 mdatabase__init ()
 {
 int
 mdatabase__init ()
 {
-  char *dir;
-  int i;
-  MPlist *plist;
-  FILE *fp;
+  MDatabaseInfo *dir_info;
+  char *path;
 
   Mchar_table = msymbol ("char-table");
 
   Mchar_table = msymbol ("char-table");
+  Masterisk = msymbol ("*");
 
 
-  mdb_dir_list = mplist ();
+  mdatabase__dir_list = mplist ();
   /** The macro M17NDIR specifies a directory where the system-wide
     MDB_DIR file exists.  */
   /** The macro M17NDIR specifies a directory where the system-wide
     MDB_DIR file exists.  */
-  if ((dir = duplicate_dirname (M17NDIR)))
-    mplist_set (mdb_dir_list, Mt, dir);
+  mplist_set (mdatabase__dir_list, Mt, get_dir_info (M17NDIR));
 
   /* The variable mdatabase_dir specifies a directory where an
      application program specific MDB_DIR file exists.  */
 
   /* The variable mdatabase_dir specifies a directory where an
      application program specific MDB_DIR file exists.  */
-  if ((dir = duplicate_dirname (mdatabase_dir)))
-    mplist_push (mdb_dir_list, Mt, dir);
+  if (mdatabase_dir && strlen (mdatabase_dir) > 0)
+    mplist_push (mdatabase__dir_list, Mt, get_dir_info (mdatabase_dir));
+
+  /* The environment variable M17NDIR specifies a directory where a
+     user specific MDB_DIR file exists.  */
+  path = getenv ("M17NDIR");
+  if (path && strlen (path) > 0)
+    mplist_push (mdatabase__dir_list, Mt, get_dir_info (path));
+  else
+    {
+      /* If the env var M17NDIR is not set, check "~/.m17n.d".  */
+      char *home = getenv ("HOME");
+      int len;
+
+      if (home
+         && (len = strlen (home))
+         && (path = alloca (len + 9)))
+       {
+         strcpy (path, home);
+         if (path[len - 1] != PATH_SEPARATOR)
+           path[len++] = PATH_SEPARATOR;
+         strcpy (path + len, ".m17n.d");
+         dir_info = get_dir_info (path);
+         mplist_push (mdatabase__dir_list, Mt, dir_info);
+       }
+      else
+       mplist_push (mdatabase__dir_list, Mt, get_dir_info (NULL));
+    }
+
+  mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
+                      mdatabase_find);
+  mdatabase__loader = (void *(*) (void *)) mdatabase_load;
+
+  mdatabase__list = mplist ();
+  mdatabase__update ();
+  return 0;
+}
+
+void
+mdatabase__fini (void)
+{
+  MPlist *plist, *p0, *p1, *p2, *p3;
 
 
-  /* The environment variable M17NDIR (if non-NULL) specifies a
-     directory where a user specific MDB_DIR file exists.  */
-  if ((dir = duplicate_dirname (getenv ("M17NDIR"))))
-    mplist_push (mdb_dir_list, Mt, dir);
+  MPLIST_DO (plist, mdatabase__dir_list)
+    free_db_info (MPLIST_VAL (plist));
+  M17N_OBJECT_UNREF (mdatabase__dir_list);
 
 
-  MLIST_INIT1 (&mdb_list, mdbs, 256);
-  MPLIST_DO (plist, mdb_dir_list)
+  /* MDATABASE_LIST ::= ((TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) ...) */
+  MPLIST_DO (plist, mdatabase__list)
     {
     {
+      p0 = MPLIST_PLIST (plist);
+      /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) */
+      MPLIST_DO (p0, MPLIST_NEXT (p0))
+       {
+         p1 = MPLIST_PLIST (p0);
+         /* P1 ::= (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) */
+         MPLIST_DO (p1, MPLIST_NEXT (p1))
+           {
+             p2 = MPLIST_PLIST (p1);
+             /* P2 ::= (TAG2 (TAG3 t:MDB) ...) */
+             MPLIST_DO (p2, MPLIST_NEXT (p2))
+               {
+                 MDatabase *mdb;
+
+                 p3 = MPLIST_PLIST (p2); /* P3 ::= (TAG3 t:MDB) */
+                 p3 = MPLIST_NEXT (p3);
+                 mdb = MPLIST_VAL (p3);
+                 if (mdb->loader == load_database)
+                   free_db_info (mdb->extra_info);
+                 free (mdb);
+               }
+           }
+       }
+    }
+  M17N_OBJECT_UNREF (mdatabase__list);
+}
+
+void
+mdatabase__update (void)
+{
+  MPlist *plist, *p0, *p1, *p2, *p3;
+  char path[PATH_MAX + 1];
+  MDatabaseInfo *dir_info;
+  struct stat statbuf;
+  int rescan = 0;
+
+  /* Update elements of mdatabase__dir_list.  */
+  MPLIST_DO (plist, mdatabase__dir_list)
+    {
+      dir_info = MPLIST_VAL (plist);
+      if (dir_info->filename)
+       {
+         if (stat (dir_info->filename, &statbuf) == 0
+             && (statbuf.st_mode & S_IFDIR))
+           {
+             if (dir_info->time < statbuf.st_mtime)
+               {
+                 rescan = 1;
+                 dir_info->time = statbuf.st_mtime;
+               }
+             if (GEN_PATH (path, dir_info->filename, dir_info->len,
+                           MDB_DIR, MDB_DIR_LEN)
+                 && stat (path, &statbuf) >= 0
+                 && dir_info->time < statbuf.st_mtime)
+               {
+                 rescan = 1;
+                 dir_info->time = statbuf.st_mtime;
+               }
+             dir_info->status = MDB_STATUS_UPDATED;
+           }
+         else
+           {
+             if (dir_info->status != MDB_STATUS_DISABLED)
+               {
+                 rescan = 1;
+                 dir_info->time = 0;
+                 dir_info->status = MDB_STATUS_DISABLED;
+               }
+           }
+       }
+    }
+
+  if (! rescan)
+    return;
+
+  /* At first, mark all databases defined automatically from mdb.dir
+     file(s) as "disabled".  */
+  MPLIST_DO (plist, mdatabase__list)
+    {
+      p0 = MPLIST_PLIST (plist);
+      /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
+      MPLIST_DO (p0, MPLIST_NEXT (p0))
+       {
+         p1 = MPLIST_PLIST (p0);
+         MPLIST_DO (p1, MPLIST_NEXT (p1))
+           {
+             p2 = MPLIST_PLIST (p1);
+             MPLIST_DO (p2, MPLIST_NEXT (p2))
+               {
+                 MDatabase *mdb;
+                 MDatabaseInfo *db_info;
+
+                 p3 = MPLIST_PLIST (p2);
+                 p3 = MPLIST_NEXT (p3);
+                 mdb = MPLIST_VAL (p3);
+                 db_info = mdb->extra_info;
+                 if (db_info->status == MDB_STATUS_AUTO)
+                   db_info->status = MDB_STATUS_DISABLED;
+               }
+           }
+       }
+    }
+
+  plist = mplist (); 
+  MPLIST_DO (p0, mdatabase__dir_list)
+    mplist_push (plist, MPLIST_KEY (p0), MPLIST_VAL (p0));
+
+  while (! MPLIST_TAIL_P (plist))
+    {
+      MDatabaseInfo *dir_info = mplist_pop (plist);
       MPlist *pl, *p;
       MPlist *pl, *p;
-      int len;
-      char path[PATH_MAX];
+      int i;
+      FILE *fp;
 
 
-      dir = (char *) MPLIST_VAL (plist);
-      len = strlen (dir);
-      if (len + MDB_DIR_LEN >= PATH_MAX)
+      if (dir_info->status == MDB_STATUS_DISABLED)
+       continue;
+      if (! GEN_PATH (path, dir_info->filename, dir_info->len,
+                     MDB_DIR, MDB_DIR_LEN))
        continue;
        continue;
-      memcpy (path, dir, len);
-      memcpy (path + len, MDB_DIR, MDB_DIR_LEN);
       if (! (fp = fopen (path, "r")))
        continue;
       if (! (fp = fopen (path, "r")))
        continue;
-      pl = mplist__from_file (fp);
+      pl = mplist__from_file (fp, NULL);
       fclose (fp);
       if (! pl)
        continue;
       MPLIST_DO (p, pl)
        {
       fclose (fp);
       if (! pl)
        continue;
       MPLIST_DO (p, pl)
        {
-         MDatabase mdb;
+         MSymbol tags[4];
          MPlist *p1;
          MPlist *p1;
+         MText *mt;
          int nbytes;
          int nbytes;
+         int with_wildcard = 0;
 
          if (! MPLIST_PLIST_P (p))
            continue;
 
          if (! MPLIST_PLIST_P (p))
            continue;
-         for (i = 0, p1 = MPLIST_PLIST (p);
-              i < 4 && MPLIST_KEY (p1) == Msymbol;
+         for (i = 0, p1 = MPLIST_PLIST (p); i < 4 && MPLIST_SYMBOL_P (p1);
               i++, p1 = MPLIST_NEXT (p1))
               i++, p1 = MPLIST_NEXT (p1))
-           mdb.tag[i] = MPLIST_SYMBOL (p1);
+           with_wildcard |= ((tags[i] = MPLIST_SYMBOL (p1)) == Masterisk);
          if (i == 0
          if (i == 0
+             || tags[0] == Masterisk
              || ! MPLIST_MTEXT_P (p1))
            continue;
          for (; i < 4; i++)
              || ! MPLIST_MTEXT_P (p1))
            continue;
          for (; i < 4; i++)
-           mdb.tag[i] = Mnil;
-         if (mdatabase_find (mdb.tag[0], mdb.tag[1],
-                             mdb.tag[2], mdb.tag[3]))
+           tags[i] = Mnil;
+         mt = MPLIST_MTEXT (p1);
+         nbytes = mtext_nbytes (mt);
+         if (nbytes > PATH_MAX)
            continue;
            continue;
+         memcpy (path, MTEXT_DATA (mt), nbytes);
+         path[nbytes] = '\0';
+         if (with_wildcard)
+           {
+             glob_t globbuf;
+             MPlist *dlist;
 
 
-         mdb.loader = load_database;
-         nbytes = mconv_encode_buffer (Mcoding_utf_8,  MPLIST_MTEXT (p1),
-                                       (unsigned char *) path, PATH_MAX);
-         if (nbytes < 0 || nbytes >= PATH_MAX)
-           continue;
-         path[nbytes++] = '\0';
-         mdb.extra_info = (void *) strdup (path);
-         MLIST_APPEND1 (&mdb_list, mdbs, mdb, MERROR_DB);
+             if (tags[0] == Mchar_table || tags[0] == Mcharset)
+               continue;
+             if (path[0] == PATH_SEPARATOR)
+               {
+                 if (glob (path, GLOB_NOSORT, NULL, &globbuf))
+                   continue;
+                 register_databases_in_files (tags, &globbuf, 0);
+                 globfree (&globbuf);
+               }
+             else
+               MPLIST_DO (dlist, mdatabase__dir_list)
+                 {
+                   MDatabaseInfo *d_info = MPLIST_VAL (dlist);
+
+                   if (d_info->status == MDB_STATUS_DISABLED)
+                     continue;
+                   if (! GEN_PATH (path, d_info->filename, d_info->len,
+                                   MTEXT_DATA (mt), nbytes))
+                     continue;
+                   if (glob (path, GLOB_NOSORT, NULL, &globbuf))
+                     continue;
+                   register_databases_in_files (tags, &globbuf, d_info->len);
+                   globfree (&globbuf);
+                 }
+           }
+         else
+           {
+             MText *version = NULL;
+
+             MPLIST_DO (p1, p1)
+               version = MPLIST_MTEXT_P (pl) ? MPLIST_MTEXT (pl) : NULL;
+             if (! version || check_version (version))
+               register_database (tags, load_database, path, MDB_STATUS_AUTO);
+           }
        }
       M17N_OBJECT_UNREF (pl);
     }
        }
       M17N_OBJECT_UNREF (pl);
     }
+  M17N_OBJECT_UNREF (plist);
+}
 
 
-  mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
-                      mdatabase_find);
-  mdatabase__loader = (void *(*) (void *)) mdatabase_load;
+MPlist *
+mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
+{
+  int mdebug_mask = MDEBUG_DATABASE;
+  MDatabaseInfo *db_info;
+  char *filename;
+  FILE *fp;
+  MPlist *plist;
+  char name[256];
 
 
-  return 0;
+  if (mdb->loader != load_database
+      || mdb->tag[0] == Mchar_table
+      || mdb->tag[0] == Mcharset)
+    MERROR (MERROR_DB, NULL);
+  MDEBUG_PRINT1 (" [DB]  <%s>.\n",
+                gen_database_name (name, mdb->tag));
+  db_info = mdb->extra_info;
+  filename = get_database_file (db_info, NULL);
+  if (! filename || ! (fp = fopen (filename, "r")))
+    MERROR (MERROR_DB, NULL);
+  plist = mplist__from_file (fp, keys);
+  fclose (fp);
+  return plist;
 }
 
 }
 
-void
-mdatabase__fini (void)
+
+/* Check if the database MDB should be reloaded or not.  It returns:
+
+       1: The database has not been updated since it was loaded last
+       time.
+
+       0: The database has never been loaded or has been updated
+       since it was loaded last time.
+
+       -1: The database is not loadable at the moment.  */
+
+int
+mdatabase__check (MDatabase *mdb)
 {
 {
-  int i;
-  MPlist *plist; 
+  MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
+  struct stat buf;
+
+  if (! get_database_file (db_info, &buf))
+    return -1;
+  if (db_info->time < buf.st_mtime)
+    return 0;
+  if (db_info->status == MDB_STATUS_AUTO
+      && db_info->filename != db_info->absolute_filename)
+    {
+      MDatabase *new;
+      
+      mdatabase__update ();
+      new = find_database (mdb->tag);
+      if (new != mdb)
+       return 0;
+    }
 
 
-  MPLIST_DO (plist, mdb_dir_list)
-    free (MPLIST_VAL (plist));
-  M17N_OBJECT_UNREF (mdb_dir_list);
+  return 1;
+}
 
 
-  for (i = 0; i < mdb_list.used; i++)
+/* Search directories in mdatabase__dir_list for file FILENAME.  If
+   the file exist, return the absolute pathname.  If FILENAME is
+   already absolute, return a copy of it.  */
+
+char *
+mdatabase__find_file (char *filename)
+{
+  struct stat buf;
+  MDatabaseInfo db_info;
+
+  if (filename[0] == PATH_SEPARATOR)
+    return (stat (filename, &buf) == 0 ? filename : NULL);
+  db_info.filename = filename;
+  db_info.len = strlen (filename);
+  db_info.time = 0;
+  db_info.absolute_filename = NULL;
+  if (! get_database_file (&db_info, &buf)
+      || stat (db_info.absolute_filename, &buf) < 0)
+    return NULL;
+  return db_info.absolute_filename;
+}
+
+char *
+mdatabase__file (MDatabase *mdb)
+{
+  MDatabaseInfo *db_info;
+
+  if (mdb->loader != load_database)
+    return NULL;
+  db_info = mdb->extra_info;
+  return get_database_file (db_info, NULL);
+}
+
+int
+mdatabase__lock (MDatabase *mdb)
+{
+  MDatabaseInfo *db_info;
+  struct stat buf;
+  FILE *fp;
+  int len;
+  char *file;
+
+  if (mdb->loader != load_database)
+    return -1;
+  db_info = mdb->extra_info;
+  if (db_info->lock_file)
+    return -1;
+  file = get_database_file (db_info, NULL);
+  if (! file)
+    return -1;
+  len = strlen (file);
+  db_info->uniq_file = malloc (len + 35);
+  if (! db_info->uniq_file)
+    return -1;
+  db_info->lock_file = malloc (len + 5);
+  if (! db_info->lock_file)
+    {
+      free (db_info->uniq_file);
+      return -1;
+    }
+  sprintf (db_info->uniq_file, "%s.%X.%X", db_info->absolute_filename,
+          (unsigned) time (NULL), (unsigned) getpid ());
+  sprintf (db_info->lock_file, "%s.LCK", db_info->absolute_filename);
+
+  fp = fopen (db_info->uniq_file, "w");
+  if (! fp)
+    {
+      char *str = strdup (db_info->uniq_file);
+      char *dir = dirname (str);
+      
+      if (stat (dir, &buf) == 0
+         || mkdir (dir, 0777) < 0
+         || ! (fp = fopen (db_info->uniq_file, "w")))
+       {
+         free (db_info->uniq_file);
+         free (db_info->lock_file);
+         db_info->lock_file = NULL;
+         free (str);
+         return -1;
+       }
+      free (str);
+    }
+  fclose (fp);
+  if (link (db_info->uniq_file, db_info->lock_file) < 0
+      && (stat (db_info->uniq_file, &buf) < 0
+         || buf.st_nlink != 2))
+    {
+      unlink (db_info->uniq_file);
+      unlink (db_info->lock_file);
+      free (db_info->uniq_file);
+      free (db_info->lock_file);
+      db_info->lock_file = NULL;
+      return 0;
+    }
+  return 1;
+}
+
+int
+mdatabase__save (MDatabase *mdb, MPlist *data)
+{
+  MDatabaseInfo *db_info;
+  FILE *fp;
+  char *file;
+  MText *mt;
+  int ret;
+
+  if (mdb->loader != load_database)
+    return -1;
+  db_info = mdb->extra_info;
+  if (! db_info->lock_file)
+    return -1;
+  file = get_database_file (db_info, NULL);
+  if (! file)
+    return -1;
+  mt = mtext ();
+  if (mplist__serialize (mt, data, 1) < 0)
+    {
+      M17N_OBJECT_UNREF (mt);
+      return -1;
+    }
+  fp = fopen (db_info->uniq_file, "w");
+  if (! fp)
     {
     {
-      MDatabase *mdb = mdb_list.mdbs + i;
+      M17N_OBJECT_UNREF (mt);
+      return -1;
+    }
+  mconv_encode_stream (msymbol ("utf-8"), mt, fp);
+  M17N_OBJECT_UNREF (mt);
+  fclose (fp);
+  if ((ret = rename (db_info->uniq_file, file)) < 0)
+    unlink (db_info->uniq_file);
+  free (db_info->uniq_file);
+  db_info->uniq_file = NULL;
+  return ret;
+}
 
 
-      if (mdb->loader == load_database)
-       free (mdb->extra_info);
+int
+mdatabase__unlock (MDatabase *mdb)
+{
+  MDatabaseInfo *db_info;
+
+  if (mdb->loader != load_database)
+    return -1;
+  db_info = mdb->extra_info;
+  if (! db_info->lock_file)
+    return -1;
+  unlink (db_info->lock_file);
+  free (db_info->lock_file);
+  db_info->lock_file = NULL;
+  if (db_info->uniq_file)
+    {
+      unlink (db_info->uniq_file);
+      free (db_info->uniq_file);
     }
     }
-  MLIST_FREE1 (&mdb_list, mdbs);
+  return 0;
 }
 
 /*** @} */
 }
 
 /*** @} */
@@ -615,12 +1254,11 @@ mdatabase__fini (void)
 /***ja
     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
 
 /***ja
     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
 
-    ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n ¥Ç¡¼
-    ¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
-    ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê
-    ¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç
-    ¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ
-    ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
+    ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n 
+    ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
+    ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
+    "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ 
+    @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
 
     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
 
 
     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
 
@@ -632,34 +1270,25 @@ char *mdatabase_dir;
 
     The mdatabase_find () function searches the m17n database for a
     data who has tags $TAG0 through $TAG3, and returns a pointer to
 
     The mdatabase_find () function searches the m17n database for a
     data who has tags $TAG0 through $TAG3, and returns a pointer to
-    the data.  If such a database is not found, it returns @c
-    NULL.  */
+    the data.  If such a data is not found, it returns @c NULL.  */
 
 /***ja
 
 /***ja
-    @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤òõ¤¹.
+    @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
 
     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
 
     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
-    $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
-    ¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
+    $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
+    @c NULL ¤òÊÖ¤¹¡£
 
     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
 
 MDatabase *
 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
 {
 
     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
 
 MDatabase *
 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
 {
-  int i;
-
-  for (i = 0; i < mdb_list.used; i++)
-    {
-      MDatabase *mdb = mdb_list.mdbs + i;
+  MSymbol tags[4];
 
 
-      if (tag0 == mdb->tag[0]
-         && tag1 == mdb->tag[1]
-         && tag2 == mdb->tag[2]
-         && tag3 == mdb->tag[3])
-       return mdb;
-    }
-  return NULL;
+  mdatabase__update ();
+  tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
+  return find_database (tags);
 }
 
 /*=*/
 }
 
 /*=*/
@@ -674,38 +1303,54 @@ mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
 /***ja
     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
 
 /***ja
     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
 
-    ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤éÆÃÄê¤Î¥¿¥°$TAG0
-    .. $TAG3 ¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn 
-    ¤¬ #Mnil ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È
-    ¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃÍ
-    ¤È¤·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
-
+    ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
+    ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
+    ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
+    plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
 
 MPlist *
 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
 {
 
 MPlist *
 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
 {
-  int i;
-  MPlist *plist = NULL, *pl;
+  MPlist *plist = mplist (), *pl = plist;
+  MPlist *p, *p0, *p1, *p2, *p3;
 
 
-  for (i = 0; i < mdb_list.used; i++)
-    {
-      MDatabase *mdb = mdb_list.mdbs + i;
+  mdatabase__update ();
 
 
-      if ((tag0 == Mnil || tag0 == mdb->tag[0])
-         && (tag1 == Mnil || tag1 == mdb->tag[1])
-         && (tag2 == Mnil || tag2 == mdb->tag[2])
-         && (tag3 == Mnil || tag3 == mdb->tag[3]))
+  MPLIST_DO (p, mdatabase__list)
+    {
+      p0 = MPLIST_PLIST (p);
+      /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
+      if (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0)
+       continue;
+      MPLIST_DO (p0, MPLIST_NEXT (p0))
        {
        {
-         if (! plist)
-           plist = pl = mplist ();
-         pl = mplist_add (pl, Mt, mdb);
+         p1 = MPLIST_PLIST (p0);
+         if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1)
+           continue;
+         MPLIST_DO (p1, MPLIST_NEXT (p1))
+           {
+             p2 = MPLIST_PLIST (p1);
+             if (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2)
+               continue;
+             MPLIST_DO (p2, MPLIST_NEXT (p2))
+               {
+                 p3 = MPLIST_PLIST (p2);
+                 if (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3)
+                   continue;
+                 p3 = MPLIST_NEXT (p3);
+                 pl = mplist_add (pl, Mt, MPLIST_VAL (p3));
+               }
+           }
        }
     }
        }
     }
+  if (MPLIST_TAIL_P (plist))
+    {
+      M17N_OBJECT_UNREF (plist);
+      plist = NULL;
+    }
   return plist;
 }
 
   return plist;
 }
 
-
-
 /*=*/
 /***en
     @brief Define a data of the m17n database.
 /*=*/
 /***en
     @brief Define a data of the m17n database.
@@ -730,21 +1375,20 @@ mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
 /***ja
     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
 
 /***ja
     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
 
-    ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉÕ²Ã
-    ¾ðÊó $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
+    ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó 
+    $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
 
 
-    $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³
-    ¤Î´Ø¿ô¤Ï mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦2 ¤Ä¤Î
-    °ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛ
-    Îó¤Ç¤¢¤ë¡£
+    $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
+    mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç 
+    $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
 
 
-    ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì
-    ¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê
-    ¤é¤Ê¤¤¡£
+    ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
+    $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
 
 
-    @return ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼
-    ¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () ¤Î°ú¿ô
-    ¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
+    @return
+    ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
+    ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
+    ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
 
     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
 
 
     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
 
@@ -758,22 +1402,13 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
                  void *extra_info)
 {
   MDatabase *mdb;
                  void *extra_info)
 {
   MDatabase *mdb;
+  MSymbol tags[4];
 
 
-  mdb = mdatabase_find (tag0, tag1, tag2, tag3);
-  if (! mdb)
-    {
-      MDatabase template;
-
-      template.tag[0] = tag0, template.tag[1] = tag1;
-      template.tag[2] = tag2, template.tag[3] = tag3;
-      MLIST_APPEND1 (&mdb_list, mdbs, template, MERROR_DB);
-      mdb = mdb_list.mdbs + (mdb_list.used - 1);
-    }
-  mdb->loader = loader ? loader : load_database;
-  mdb->extra_info = extra_info;
-  if (mdb->loader == load_database)
-    mdb->extra_info = strdup ((char *) extra_info);
-  return (&(mdb_list.mdbs[mdb_list.used - 1]));
+  tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
+  if (! loader)
+    loader = load_database;
+  mdb = register_database (tags, loader, extra_info, MDB_STATUS_EXPLICIT);
+  return mdb;
 }
 
 /*=*/
 }
 
 /*=*/
@@ -784,10 +1419,10 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
     returns the contents.  The type of contents depends on the type of
     the data.
 
     returns the contents.  The type of contents depends on the type of
     the data.
 
-    If the data is of the @e plist type, this function returns a
+    If the data is of the @e plist @e type, this function returns a
     pointer to @e plist.
 
     pointer to @e plist.
 
-    If the database is of the @e chartable type, it returns a
+    If the database is of the @e chartable @e type, it returns a
     chartable.  The default value of the chartable is set according to
     the second tag of the data as below:
 
     chartable.  The default value of the chartable is set according to
     the second tag of the data as below:
 
@@ -795,7 +1430,7 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
     @li If the tag is #Minteger, the default value is -1.
     @li Otherwise, the default value is @c NULL.
 
     @li If the tag is #Minteger, the default value is -1.
     @li Otherwise, the default value is @c NULL.
 
-    If the data is of the @e charset type, it returns a plist of length 2
+    If the data is of the @e charset @e type, it returns a plist of length 2
     (keys are both #Mt).  The value of the first element is an array
     of integers that maps code points to the corresponding character
     codes.  The value of the second element is a chartable of integers
     (keys are both #Mt).  The value of the first element is an array
     of integers that maps code points to the corresponding character
     codes.  The value of the second element is a chartable of integers
@@ -806,22 +1441,22 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
 /***ja
     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
 
 /***ja
     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
 
-    ´Ø¿ô mdatabase_load () ¤Ï $MDB ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤Î
-    Ãæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
+    ´Ø¿ô mdatabase_load () ¤Ï $MDB 
+    ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
 
 
-    ¥Ç¡¼¥¿¤¬ @e plist ¥¿¥¤¥×¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
+    ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
 
 
-    ¥Ç¡¼¥¿¤¬ @e chartable ¥¿¥¤¥×¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£Ê¸»ú¥Æ¡¼¥Ö¥ë
-    ¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
+    ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
+    Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
 
     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
 
 
     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
 
-    ¥Ç¡¼¥¿¤¬ @e charset ¥¿¥¤¥×¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë 
-    #Mt ¡Ë¡£ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×
-    ¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡££²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö
-    ¥ë¤Ç¤¢¤ë¡£¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
+    ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
+    ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
+    £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
+    ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
 
     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
   */
 
     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
   */
@@ -833,11 +1468,6 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
 void *
 mdatabase_load (MDatabase *mdb)
 {
 void *
 mdatabase_load (MDatabase *mdb)
 {
-  int mdebug_mask = MDEBUG_DATABASE;
-  char buf[256];
-
-  MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
-                gen_database_name (buf, mdb->tag));
   return (*mdb->loader) (mdb->tag, mdb->extra_info);
 }
 
   return (*mdb->loader) (mdb->tag, mdb->extra_info);
 }
 
@@ -849,11 +1479,11 @@ mdatabase_load (MDatabase *mdb)
     that identify the data in $MDB.  The length of the array is
     four.  */
 
     that identify the data in $MDB.  The length of the array is
     four.  */
 
-/***oldja
+/***ja
     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
 
     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
 
-    ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ
-    ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï 4 ¤Ç¤¢¤ë¡£
+    ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
+    4 ¤Ç¤¢¤ë¡£
 
     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
 
 
     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */