Update copyright years
[m17n/m17n-lib.git] / src / database.c
index a274458..ca33ec4 100644 (file)
@@ -1,5 +1,5 @@
 /* database.c -- database module.
-   Copyright (C) 2003, 2004
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H15PRO112
 
 
    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.
 
-    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
-    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.
 
     @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" ¤È¤¤¤¦¥Ç¥£¥ì¥¯¥È¥ê¡Ë¤ËÊ̤Υǡ¼¥¿¤òÃÖ¤¯¡£
+
+    m17n 
+    ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤ÏÊ£¿ô¤Î¿Íͤʥǡ¼¥¿¤¬´Þ¤Þ¤ì¤Æ¤ª¤ê¡¢³Æ¥Ç¡¼¥¿¤Ï
+    TAG0, TAG1, TAG2, TAG3¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Î£´¤Ä¤Î¥¿¥°¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£
 
-    TAG0 ¤Ï¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤ò°Ê²¼¤Î¤è¤¦¤Ë»ØÄꤹ¤ë¡£
+    TAG0 ¤Ë¤è¤Ã¤Æ¡¢¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ï¼¡¤Î¤è¤¦¤Ë»ØÄꤵ¤ì¤ë¡£
 
     @li 
-    TAG0 ¤¬ #Mchar_table ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e chartable¥¿¥¤¥× ¤È¸Æ¤Ð¤ì¡¢
-    ³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë
-    ¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Msymbol, #Minteger, #Mstring, #Mtext, #Mplist ¤Î
-    ¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
+    TAG0 ¤¬ #Mchar_table ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e chartable¥¿¥¤¥× 
+    ¤È¸Æ¤Ð¤ì¡¢³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç
+    TAG1 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Msymbol, #Minteger, #Mstring,
+    #Mtext, #Mplist ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
 
     @li 
-    TAG0 ¤¬ #Mcharset ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e charset¥¿¥¤¥× ¤È¸Æ¤Ð¤ì¡¢Ê¸
-    »ú¥»¥Ã¥ÈÍѤΥǥ³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥Þ¥Ã¥×¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1 ¤Ï
-    Ê¸»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó
-    ¥Ü¥ë¤Ç¤è¤¤¡£
+    TAG0 ¤¬ #Mcharset ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e charset¥¿¥¤¥× 
+    ¤È¸Æ¤Ð¤ì¡¢Ê¸»ú¥»¥Ã¥ÈÍѤΥǥ³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥Þ¥Ã¥×¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1
+    ¤Ïʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£TAG2 ¤È TAG3
+    ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
 
     @li
     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
 */
 #include <sys/stat.h>
 #include <unistd.h>
 #include <limits.h>
+#include <glob.h>
+#include <time.h>
+#include <libgen.h>
 
-#include "m17n.h"
+#include "m17n-core.h"
 #include "m17n-misc.h"
 #include "internal.h"
 #include "mtext.h"
 #include "character.h"
-#include "charset.h"
 #include "database.h"
-#include "coding.h"
 #include "plist.h"
 
 /** 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
+
+#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;
+static MSymbol Mversion;
 
 /** Structure for a data in the m17n database.  */
 
@@ -152,15 +178,7 @@ struct MDatabase
   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)
@@ -246,13 +264,13 @@ load_chartable (FILE *fp, MSymbol type)
        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)
-       break;
+       continue;
 
       if (type == Mstring)
        {
@@ -278,9 +296,7 @@ load_chartable (FILE *fp, MSymbol type)
          /* VAL is an M-text.  */
          MText *mt;
          if (c == '"')
-           mt = mconv_decode_buffer (Mcoding_utf_8,
-                                     (unsigned char *) (buf + i),
-                                     len - i - 1);
+           mt = mtext__from_data (buf + i, len - i - 1, MTEXT_FORMAT_UTF_8, 1);
          else
            {
              mt = mtext ();
@@ -291,6 +307,18 @@ load_chartable (FILE *fp, MSymbol type)
        }
       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
@@ -317,84 +345,6 @@ load_chartable (FILE *fp, MSymbol type)
 }
 
 
-/** Load a data of type @c charset from the file FD.  */
-
-static void *
-load_charset (FILE *fp, MSymbol charset_name)
-{
-  MCharset *charset = MCHARSET (charset_name);
-  int *decoder;
-  MCharTable *encoder;
-  int size;
-  int i, c;
-  int found = 0;
-  MPlist *plist;
-
-  if (! charset)
-    MERROR (MERROR_DB, NULL);
-  size = (charset->code_range[15]
-         - (charset->min_code - charset->code_range_min_code));
-  MTABLE_MALLOC (decoder, size, MERROR_DB);
-  for (i = 0; i < size; i++)
-    decoder[i] = -1;
-  encoder = mchartable (Minteger, (void *) MCHAR_INVALID_CODE);
-
-  while ((c = getc (fp)) != EOF)
-    {
-      unsigned code1, code2, c1, c2;
-      int idx1, idx2;
-      char buf[256];
-
-      ungetc (c, fp);
-      fgets (buf, 256, fp);
-      if (c != '#')
-       {
-         if (sscanf (buf, "0x%x-0x%x 0x%x", &code1, &code2, &c1) == 3)
-           {
-             idx1 = CODE_POINT_TO_INDEX (charset, code1);
-             if (idx1 >= size)
-               continue;
-             idx2 = CODE_POINT_TO_INDEX (charset, code2);
-             if (idx2 >= size)
-               idx2 = size - 1;
-             c2 = c1 + (idx2 - idx1);
-           }
-         else if (sscanf (buf, "0x%x 0x%x", &code1, &c1) == 2)
-           {
-             idx1 = idx2 = CODE_POINT_TO_INDEX (charset, code1);
-             if (idx1 >= size)
-               continue;
-             c2 = c1;
-           }
-         else
-           continue;
-         if (idx1 >= 0 && idx2 >= 0)
-           {
-             decoder[idx1] = c1;
-             mchartable_set (encoder, c1, (void *) code1);
-             for (idx1++, c1++; idx1 <= idx2; idx1++, c1++)
-               {
-                 code1 = INDEX_TO_CODE_POINT (charset, idx1);
-                 decoder[idx1] = c1;
-                 mchartable_set (encoder, c1, (void *) code1);
-               }
-             found++;
-           }
-       }
-    }
-
-  if (! found)
-    {
-      free (decoder);
-      M17N_OBJECT_UNREF (encoder);
-      return NULL;
-    }
-  plist = mplist ();
-  mplist_add (plist, Mt, decoder);
-  mplist_add (plist, Mt, encoder);
-  return plist;
-}
-
 static char *
 gen_database_name (char *buf, MSymbol *tags)
 {
@@ -403,82 +353,409 @@ gen_database_name (char *buf, MSymbol *tags)
   strcpy (buf, msymbol_name (tags[0]));
   for (i = 1; i < 4; i++)
     {
-      strcat (buf, ", ");
+      strcat (buf, ",");
       strcat (buf, msymbol_name (tags[i]));
     }
   return buf;
 }
 
-static FILE *
-get_database_stream (char *filename)
-{
-  FILE *fp = NULL;
+/* Return the absolute file name for DB_INFO->filename or NULL if no
+   absolute file name was found.  If BUF is non-NULL, store the result
+   of `stat' call in it.  In that case, set *RESULT to the return
+   value of `stat'.  */
 
-  if (filename[0] == '/')
-    fp = fopen (filename, "r");
+char *
+get_database_file (MDatabaseInfo *db_info, struct stat *buf, int *result)
+{
+  if (db_info->absolute_filename)
+    {
+      if (buf)
+       *result = stat (db_info->absolute_filename, buf);
+    }
   else
     {
+      struct stat stat_buf;
+      struct stat *statbuf = buf ? buf : &stat_buf;
+      int res;
       MPlist *plist;
-      char path[PATH_MAX];
+      char path[PATH_MAX + 1];
 
       MPLIST_DO (plist, mdatabase__dir_list)
        {
-         strcpy (path, (char *) MPLIST_VAL (plist));
-         strcat (path, filename);
-         fp = fopen (path, "r");
-         if (fp)
-           break;
+         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)
+             && (res = stat (path, statbuf)) == 0)
+           {
+             db_info->absolute_filename = strdup (path);
+             if (result)
+               *result = res;
+             break;
+           }
        }
     }
-  return fp;
+
+  return db_info->absolute_filename;
 }
 
 static void *
 load_database (MSymbol *tags, void *extra_info)
 {
-  FILE *fp = get_database_stream ((char *) extra_info);
+  MDatabaseInfo *db_info = extra_info;
   void *value;
+  char *filename = get_database_file (db_info, NULL, NULL);
+  FILE *fp;
+  int mdebug_flag = MDEBUG_DATABASE;
+  char buf[256];
 
-  if (! fp)
-    MERROR (MERROR_DB, NULL);
+  MDEBUG_PRINT1 (" [DB] <%s>", gen_database_name (buf, tags));
+  if (! filename || ! (fp = fopen (filename, "r")))
+    {
+      if (filename)
+       MDEBUG_PRINT1 (" open fail: %s\n", filename);
+      else
+       MDEBUG_PRINT1 (" not found: %s\n", db_info->filename);
+      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]);
+    {
+      if (! mdatabase__load_charset_func)
+       MERROR (MERROR_DB, NULL);
+      value = (*mdatabase__load_charset_func) (fp, tags[1]);
+    }
   else
     value = mplist__from_file (fp, NULL);
   fclose (fp);
 
   if (! value)
     MERROR (MERROR_DB, NULL);
+  db_info->time = time (NULL);
   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;
+}
+
+static void register_databases_in_files (MSymbol tags[4],
+                                        char *filename, int len);
 
-  if (! dirname
-      || stat (dirname, &buf) < 0)
+static MDatabase *
+find_database (MSymbol tags[4])
+{
+  MPlist *plist;
+  int i;
+  MDatabase *mdb;
+  
+  if (! mdatabase__list)
     return NULL;
+  for (i = 0, plist = mdatabase__list; i < 4; i++)
+    {
+      MPlist *pl = mplist__assq (plist, tags[i]);
+      MPlist *p;
+
+      if ((p = mplist__assq (plist, Masterisk)))
+       {
+         MDatabaseInfo *db_info;
+         int j;
+
+         p = MPLIST_PLIST (p);
+         for (j = i + 1; j < 4; j++)
+           p = MPLIST_PLIST (MPLIST_NEXT (p));
+         mdb = MPLIST_VAL (MPLIST_NEXT (p));
+         db_info = mdb->extra_info;
+         if (db_info->status != MDB_STATUS_DISABLED)
+           {
+             register_databases_in_files (mdb->tag,
+                                          db_info->filename, db_info->len);
+             db_info->status = MDB_STATUS_DISABLED;
+             return find_database (tags);
+           }
+       }
+      if (! pl)
+       return NULL;
+      plist = MPLIST_PLIST (pl);
+      plist = MPLIST_NEXT (plist);
+    }
+  mdb = MPLIST_VAL (plist);
+  return mdb;
+}
+
+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);
+  M17N_OBJECT_UNREF (db_info->properties);
+  free (db_info);
+}
 
-  len = strlen (dirname);
-  MTABLE_MALLOC (str, len + 2, MERROR_DB);
-  memcpy (str, dirname, len + 1);
-  if (str[len - 1] != '/')
+static int
+check_version (MText *version)
+{
+  char *verstr = (char *) MTEXT_DATA (version);
+  char *endp = verstr + mtext_nbytes (version);
+  int ver[3];
+  int i;
+
+  ver[0] = ver[1] = ver[2] = 0;
+  for (i = 0; verstr < endp; verstr++)
+    {
+      if (*verstr == '.')
+       {
+         i++;
+         if (i == 3)
+           break;
+         continue;
+       }
+      if (! isdigit (*verstr))
+       break;
+      ver[i] = ver[i] * 10 + (*verstr - '0');
+    }
+  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,
+                  MPlist *properties)
+{
+  MDatabase *mdb;
+  MDatabaseInfo *db_info;
+  int i;
+  MPlist *plist;
+
+  if (properties)
+    {
+      MPLIST_DO (plist, properties)
+       if (MPLIST_PLIST_P (plist))
+         {
+           MPlist *p = MPLIST_PLIST (plist);
+
+           if (MPLIST_SYMBOL_P (p)
+               && MPLIST_SYMBOL (p) == Mversion
+               && MPLIST_MTEXT_P (MPLIST_NEXT (p)))
+             {
+               if (check_version (MPLIST_MTEXT (MPLIST_NEXT (p))))
+                 break;
+               return NULL;
+             }
+         }
+    }
+
+  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;
+      M17N_OBJECT_UNREF (db_info->properties);
+      if (properties)
+       {
+         db_info->properties = properties;
+         M17N_OBJECT_REF (properties);
+       }
+    }
+
+  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], char *filename, int len)
+{
+  int i, j;
+  MPlist *load_key = mplist ();
+  FILE *fp;
+  MPlist *plist, *pl;
+
+  MPLIST_DO (plist, mdatabase__dir_list)
+    {
+      glob_t globbuf;
+      int headlen;
+
+      if (filename[0] == PATH_SEPARATOR)
+       {
+         if (glob (filename, GLOB_NOSORT, NULL, &globbuf))
+           break;
+         headlen = 0;
+       }
+      else
+       {
+         MDatabaseInfo *d_info = MPLIST_VAL (plist);
+         char path[PATH_MAX + 1];
+
+         if (d_info->status == MDB_STATUS_DISABLED)
+           continue;
+         if (! GEN_PATH (path, d_info->filename, d_info->len, filename, len))
+           continue;
+         if (glob (path, GLOB_NOSORT, NULL, &globbuf))
+           continue;
+         headlen = d_info->len;
+       }
+
+      for (i = 0; i < globbuf.gl_pathc; i++)
+       {
+         if (! (fp = fopen (globbuf.gl_pathv[i], "r")))
+           continue;
+         pl = mplist__from_file (fp, load_key);
+         fclose (fp);
+         if (! pl)
+           continue;
+         if (MPLIST_PLIST_P (pl))
+           {
+             MPlist *p;
+             MSymbol tags2[4];
+
+             for (j = 0, p = MPLIST_PLIST (pl); j < 4 && MPLIST_SYMBOL_P (p);
+                  j++, p = MPLIST_NEXT (p))
+               tags2[j] = MPLIST_SYMBOL (p);
+             for (; j < 4; j++)
+               tags2[j] = Mnil;
+             for (j = 0; j < 4; j++)
+               if (tags[j] != Masterisk && tags[j] != tags2[j])
+                 break;
+             if (j == 4)
+               register_database (tags2, load_database,
+                                  globbuf.gl_pathv[i] + headlen,
+                                  MDB_STATUS_AUTO, p);
+           }
+         M17N_OBJECT_UNREF (pl);
+       }
+      globfree (&globbuf);
+      if (filename[0] == PATH_SEPARATOR)
+       break;
+    }
+  M17N_OBJECT_UNREF (load_key);
+}
+
+static int
+expand_wildcard_database (MPlist *plist)
+{
+  MDatabase *mdb;
+  MDatabaseInfo *db_info;
+
+  plist = MPLIST_NEXT (plist);
+  while (MPLIST_PLIST_P (plist))
+    {
+      plist = MPLIST_PLIST (plist);
+      plist = MPLIST_NEXT (plist);
+    }
+  mdb = MPLIST_VAL (plist);
+  if (mdb->loader == load_database
+      && (db_info = mdb->extra_info)
+      && db_info->status != MDB_STATUS_DISABLED)
     {
-      str[len] = '/';
-      str[len + 1] = '\0';
+      register_databases_in_files (mdb->tag, db_info->filename, db_info->len);
+      db_info->status = MDB_STATUS_DISABLED;
+      return 1;
     }
-  return str;
+  return 0;
 }
 
 \f
@@ -487,48 +764,193 @@ duplicate_dirname (char *dirname)
 /** List of database directories.  */ 
 MPlist *mdatabase__dir_list;
 
-MSymbol M_database_hook;
+void *(*mdatabase__load_charset_func) (FILE *fp, MSymbol charset_name);
 
 int
 mdatabase__init ()
 {
-  char *dir;
-  int i;
-  MPlist *plist;
-  FILE *fp;
+  MDatabaseInfo *dir_info;
+  char *path;
+
+  mdatabase__load_charset_func = NULL;
 
   Mchar_table = msymbol ("char-table");
-  M_database_hook = msymbol ("  database-hook");
+  Mcharset = msymbol ("charset");
+  Masterisk = msymbol ("*");
+  Mversion = msymbol ("version");
 
   mdatabase__dir_list = mplist ();
   /** The macro M17NDIR specifies a directory where the system-wide
     MDB_DIR file exists.  */
-  if ((dir = duplicate_dirname (M17NDIR)))
-    mplist_set (mdatabase__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.  */
-  if ((dir = duplicate_dirname (mdatabase_dir)))
-    mplist_push (mdatabase__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;
 
-  /* 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 (mdatabase__dir_list, Mt, dir);
+      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));
+    }
 
-  MLIST_INIT1 (&mdb_list, mdbs, 256);
+  mdatabase__list = mplist ();
+  mdatabase__update ();
+  return 0;
+}
+
+void
+mdatabase__fini (void)
+{
+  MPlist *plist, *p0, *p1, *p2, *p3;
+
+  MPLIST_DO (plist, mdatabase__dir_list)
+    free_db_info (MPLIST_VAL (plist));
+  M17N_OBJECT_UNREF (mdatabase__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;
-      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;
-      memcpy (path, dir, len);
-      memcpy (path + len, MDB_DIR, MDB_DIR_LEN);
       if (! (fp = fopen (path, "r")))
        continue;
       pl = mplist__from_file (fp, NULL);
@@ -537,86 +959,266 @@ mdatabase__init ()
        continue;
       MPLIST_DO (p, pl)
        {
-         MDatabase mdb;
+         MSymbol tags[4];
          MPlist *p1;
+         MText *mt;
          int nbytes;
+         int with_wildcard = 0;
 
          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))
-           mdb.tag[i] = MPLIST_SYMBOL (p1);
+           with_wildcard |= ((tags[i] = MPLIST_SYMBOL (p1)) == Masterisk);
          if (i == 0
+             || tags[0] == Masterisk
              || ! 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] = with_wildcard ? Masterisk : Mnil;
+         mt = MPLIST_MTEXT (p1);
+         nbytes = mtext_nbytes (mt);
+         if (nbytes > PATH_MAX)
            continue;
-
-         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);
+         memcpy (path, MTEXT_DATA (mt), nbytes);
+         path[nbytes] = '\0';
+         if (with_wildcard)
+           register_database (tags, load_database, path,
+                              MDB_STATUS_AUTO_WILDCARD, NULL);
+         else
+           register_database (tags, load_database, path,
+                              MDB_STATUS_AUTO, p1);
        }
       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_flag = 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, 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;
+  int result;
 
-  MPLIST_DO (plist, mdatabase__dir_list)
-    free (MPLIST_VAL (plist));
-  M17N_OBJECT_UNREF (mdatabase__dir_list);
+  if (db_info->absolute_filename != db_info->filename
+      || db_info->status == MDB_STATUS_AUTO)
+    mdatabase__update ();
+
+  if (! get_database_file (db_info, &buf, &result)
+      || result < 0)
+    return -1;
+  if (db_info->time < buf.st_mtime)
+    return 0;
+  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;
+  int result;
+  MDatabaseInfo db_info;
+
+  if (filename[0] == PATH_SEPARATOR)
+    return (stat (filename, &buf) == 0 ? strdup (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, &result)
+      || result < 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, 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, 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)
     {
-      MDatabase *mdb = mdb_list.mdbs + i;
+      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);
 
-      if (mdb->loader == load_database)
-       free (mdb->extra_info);
+  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;
     }
-  MLIST_FREE1 (&mdb_list, mdbs);
+  return 1;
 }
 
-MPlist *
-mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
+int
+mdatabase__save (MDatabase *mdb, MPlist *data)
 {
-  int mdebug_mask = MDEBUG_DATABASE;
+  MDatabaseInfo *db_info;
   FILE *fp;
-  MPlist *plist;
-  char buf[256];
+  char *file;
+  MText *mt;
+  int ret;
 
-  if (mdb->loader != load_database
-      || mdb->tag[0] == Mchar_table
-      || mdb->tag[0] == Mcharset)
-    MERROR (MERROR_DB, NULL);
-  MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
-                gen_database_name (buf, mdb->tag));
-  fp = get_database_stream ((char *) mdb->extra_info);
+  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, 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)
-    MERROR (MERROR_DB, NULL);
-  plist = mplist__from_file (fp, keys);
+    {
+      M17N_OBJECT_UNREF (mt);
+      return -1;
+    }
+  if (mt->format > MTEXT_FORMAT_UTF_8)
+    mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
+  fwrite (MTEXT_DATA (mt), 1, mtext_nchars (mt), fp);
   fclose (fp);
-  return plist;
+  M17N_OBJECT_UNREF (mt);
+  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;
 }
 
+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);
+    }
+  return 0;
+}
+
+MPlist *
+mdatabase__props (MDatabase *mdb)
+{
+  MDatabaseInfo *db_info;
+
+  if (mdb->loader != load_database)
+    return NULL;
+  db_info = mdb->extra_info;
+  return db_info->properties;
+}
 
 /*** @} */
 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
@@ -624,6 +1226,28 @@ mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
 \f
 /* External API */
 
+/*** @addtogroup m17nCharset */
+/*** @{ */
+/*=*/
+/***en
+    @brief The symbol @c Mcharset.
+
+    Any decoded M-text has a text property whose key is the predefined
+    symbol @c Mcharset.  The name of @c Mcharset is
+    <tt>"charset"</tt>.  */
+
+/***ja
+    @brief ¥·¥ó¥Ü¥ë @c Mcharset.
+
+    ¥Ç¥³¡¼¥É¤µ¤ì¤¿ M-text ¤Ï¡¢¥­¡¼¤¬ @c Mcharset
+    ¤Ç¤¢¤ë¤è¤¦¤Ê¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£
+    ¥·¥ó¥Ü¥ë @c Mcharset ¤Ï <tt>"charset"</tt> ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä¡£  */
+
+MSymbol Mcharset;
+/*=*/
+/*** @} */
+/*=*/
+
 /*** @addtogroup m17nDatabase */
 /*** @{ */
 
@@ -642,12 +1266,11 @@ mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
 /***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 ¤Ç¤¢¤ë¡£  */
 
@@ -665,32 +1288,19 @@ char *mdatabase_dir;
     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
 
     ´Ø¿ô 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)
 {
-  int i;
-  MDatabaseHookFunc func
-    = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook);
-
-  if (func)
-    func (tag0, tag1, tag2, tag3);
-
-  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);
 }
 
 /*=*/
@@ -706,42 +1316,78 @@ mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
 
     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
-    ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn 
-    ¤¬ #Mnil ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È
-    ¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃÍ
-    ¤È¤·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
-
+    ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
+    ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
+    plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
 
 MPlist *
 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
 {
-  int i;
-  MPlist *plist = NULL, *pl;
-  MDatabaseHookFunc func
-    = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook);
+  MPlist *plist = mplist (), *pl = plist;
+  MPlist *p, *p0, *p1, *p2, *p3;
 
-  if (func)
-    func (tag0, tag1, tag2, tag3);
+  mdatabase__update ();
 
-  for (i = 0; i < mdb_list.used; i++)
+  MPLIST_DO (p, mdatabase__list)
     {
-      MDatabase *mdb = mdb_list.mdbs + i;
-
-      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]))
+      p0 = MPLIST_PLIST (p);
+      /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
+      if (MPLIST_SYMBOL (p0) == Masterisk
+         || (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 (MPLIST_SYMBOL (p1) == Masterisk)
+           {
+             if (expand_wildcard_database (p1))
+               {
+                 M17N_OBJECT_UNREF (plist);
+                 return mdatabase_list (tag0, tag1, tag2, tag3);
+               }
+             continue;
+           }
+         if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1)
+           continue;
+         MPLIST_DO (p1, MPLIST_NEXT (p1))
+           {
+             p2 = MPLIST_PLIST (p1);
+             if (MPLIST_SYMBOL (p2) == Masterisk)
+               {
+                 if (expand_wildcard_database (p2))
+                   {
+                     M17N_OBJECT_UNREF (plist);
+                     return mdatabase_list (tag0, tag1, tag2, tag3);
+                   }
+                 continue;
+               }
+             if (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2)
+               continue;
+             MPLIST_DO (p2, MPLIST_NEXT (p2))
+               {
+                 p3 = MPLIST_PLIST (p2);
+                 if (MPLIST_SYMBOL (p3) == Masterisk)
+                   {
+                     if (expand_wildcard_database (p3))
+                       {
+                         M17N_OBJECT_UNREF (plist);
+                         return mdatabase_list (tag0, tag1, tag2, tag3);
+                       }
+                     continue;
+                   }
+                 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);
   return plist;
 }
 
-
-
 /*=*/
 /***en
     @brief Define a data of the m17n database.
@@ -766,22 +1412,20 @@ mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
 /***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 ¤òÊÖ¤¹¡£
+    ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
+    ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
+    ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
 
     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
 
@@ -795,33 +1439,13 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
                  void *extra_info)
 {
   MDatabase *mdb;
-  MDatabaseHookFunc func
-    = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook);
-
-  if (func)
-    func (tag0, tag1, tag2, tag3);
+  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;
-      template.extra_info = NULL;
-      MLIST_APPEND1 (&mdb_list, mdbs, template, MERROR_DB);
-      mdb = mdb_list.mdbs + (mdb_list.used - 1);
-    }
-  mdb->loader = loader ? loader : load_database;
-  if (mdb->loader == load_database)
-    {
-      if (mdb->extra_info)
-       free (mdb->extra_info);
-      mdb->extra_info = strdup ((char *) extra_info);
-    }
-  else
-    mdb->extra_info = 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, NULL);
+  return mdb;
 }
 
 /*=*/
@@ -854,22 +1478,22 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
 /***ja
     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
 
-    ´Ø¿ô mdatabase_load () ¤Ï $MDB ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤Î
-    Ãæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
+    ´Ø¿ô mdatabase_load () ¤Ï $MDB 
+    ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
 
     ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
 
-    ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£Ê¸»ú¥Æ¡¼¥Ö¥ë
-    ¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
+    ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
+    Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
 
     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
 
-    ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë 
-    #Mt ¡Ë¡£ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×
-    ¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡££²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö
-    ¥ë¤Ç¤¢¤ë¡£¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
+    ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
+    ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
+    £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
+    ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
 
     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
   */
@@ -881,11 +1505,6 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
 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);
 }
 
@@ -900,8 +1519,8 @@ mdatabase_load (MDatabase *mdb)
 /***ja
     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
 
-    ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ
-    ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï 4 ¤Ç¤¢¤ë¡£
+    ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
+    4 ¤Ç¤¢¤ë¡£
 
     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */