Update copyright years
[m17n/m17n-lib.git] / src / database.c
index 0f7891c..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
 
 #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.  */
       path[dir_len + file_len] = '\0', 1))
 
 static MSymbol Masterisk;
+static MSymbol Mversion;
 
 /** Structure for a data in the m17n database.  */
 
@@ -297,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 ();
@@ -348,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)
 {
@@ -440,46 +359,42 @@ gen_database_name (char *buf, MSymbol *tags)
   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.  */
+/* 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'.  */
 
 char *
-get_database_file (MDatabaseInfo *db_info, struct stat *buf)
+get_database_file (MDatabaseInfo *db_info, struct stat *buf, int *result)
 {
-  if (db_info->status == MDB_STATUS_DISABLED)
-    return NULL;
   if (db_info->absolute_filename)
     {
       if (buf)
-       stat (db_info->absolute_filename, 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 + 1];
 
-      db_info->absolute_filename = find_file (db_info, statbuf);
+      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)
+             && (res = stat (path, statbuf)) == 0)
+           {
+             db_info->absolute_filename = strdup (path);
+             if (result)
+               *result = res;
+             break;
+           }
+       }
     }
 
   return db_info->absolute_filename;
@@ -490,9 +405,9 @@ load_database (MSymbol *tags, void *extra_info)
 {
   MDatabaseInfo *db_info = extra_info;
   void *value;
-  char *filename = get_database_file (db_info, NULL);
+  char *filename = get_database_file (db_info, NULL, NULL);
   FILE *fp;
-  int mdebug_mask = MDEBUG_DATABASE;
+  int mdebug_flag = MDEBUG_DATABASE;
   char buf[256];
 
   MDEBUG_PRINT1 (" [DB] <%s>", gen_database_name (buf, tags));
@@ -510,7 +425,11 @@ load_database (MSymbol *tags, void *extra_info)
   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);
@@ -555,23 +474,48 @@ get_dir_info (char *dirname)
   return dir_info;
 }
 
+static void register_databases_in_files (MSymbol tags[4],
+                                        char *filename, int len);
+
 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++)
     {
-      plist = mplist__assq (plist, tags[i]);
-      if (! plist)
+      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 (plist);
+      plist = MPLIST_PLIST (pl);
       plist = MPLIST_NEXT (plist);
     }
-  return MPLIST_VAL (plist);
+  mdb = MPLIST_VAL (plist);
+  return mdb;
 }
 
 static void
@@ -581,6 +525,7 @@ free_db_info (MDatabaseInfo *db_info)
   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);
 }
 
@@ -616,15 +561,31 @@ check_version (MText *version)
 static MDatabase *
 register_database (MSymbol tags[4],
                   void *(*loader) (MSymbol *, void *),
-                  void *extra_info, enum MDatabaseStatus status)
+                  void *extra_info, enum MDatabaseStatus status,
+                  MPlist *properties)
 {
   MDatabase *mdb;
   MDatabaseInfo *db_info;
   int i;
   MPlist *plist;
 
-  if (! mdatabase__list)
-    mdatabase__list = mplist ();
+  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++)
     {
@@ -688,6 +649,12 @@ register_database (MSymbol tags[4],
        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
@@ -700,66 +667,117 @@ register_database (MSymbol tags[4],
 }
 
 static void
-register_databases_in_files (MSymbol tags[4], glob_t *globbuf, int headlen)
+register_databases_in_files (MSymbol tags[4], char *filename, int len)
 {
   int i, j;
   MPlist *load_key = mplist ();
   FILE *fp;
-  MPlist *plist;
+  MPlist *plist, *pl;
 
-  for (i = 0; i < globbuf->gl_pathc; i++)
+  MPLIST_DO (plist, mdatabase__dir_list)
     {
-      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))
+      glob_t globbuf;
+      int headlen;
+
+      if (filename[0] == PATH_SEPARATOR)
        {
-         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;
+         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;
+       }
 
-             MPLIST_DO (pl, pl)
-               version = MPLIST_MTEXT_P (pl) ? MPLIST_MTEXT (pl) : NULL;
-             if (! version || check_version (version))
+      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);
+                                  globbuf.gl_pathv[i] + headlen,
+                                  MDB_STATUS_AUTO, p);
            }
+         M17N_OBJECT_UNREF (pl);
        }
-      M17N_OBJECT_UNREF (plist);
+      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)
+    {
+      register_databases_in_files (mdb->tag, db_info->filename, db_info->len);
+      db_info->status = MDB_STATUS_DISABLED;
+      return 1;
+    }
+  return 0;
+}
+
 \f
 /* Internal API */
 
 /** List of database directories.  */ 
 MPlist *mdatabase__dir_list;
 
+void *(*mdatabase__load_charset_func) (FILE *fp, MSymbol charset_name);
+
 int
 mdatabase__init ()
 {
   MDatabaseInfo *dir_info;
   char *path;
 
+  mdatabase__load_charset_func = NULL;
+
   Mchar_table = msymbol ("char-table");
+  Mcharset = msymbol ("charset");
   Masterisk = msymbol ("*");
+  Mversion = msymbol ("version");
 
   mdatabase__dir_list = mplist ();
   /** The macro M17NDIR specifies a directory where the system-wide
@@ -797,10 +815,6 @@ mdatabase__init ()
        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;
@@ -961,7 +975,7 @@ mdatabase__update (void)
              || ! MPLIST_MTEXT_P (p1))
            continue;
          for (; i < 4; i++)
-           tags[i] = Mnil;
+           tags[i] = with_wildcard ? Masterisk : Mnil;
          mt = MPLIST_MTEXT (p1);
          nbytes = mtext_nbytes (mt);
          if (nbytes > PATH_MAX)
@@ -969,44 +983,11 @@ mdatabase__update (void)
          memcpy (path, MTEXT_DATA (mt), nbytes);
          path[nbytes] = '\0';
          if (with_wildcard)
-           {
-             glob_t globbuf;
-             MPlist *dlist;
-
-             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);
-                 }
-           }
+           register_database (tags, load_database, path,
+                              MDB_STATUS_AUTO_WILDCARD, NULL);
          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);
-           }
+           register_database (tags, load_database, path,
+                              MDB_STATUS_AUTO, p1);
        }
       M17N_OBJECT_UNREF (pl);
     }
@@ -1016,7 +997,7 @@ mdatabase__update (void)
 MPlist *
 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
 {
-  int mdebug_mask = MDEBUG_DATABASE;
+  int mdebug_flag = MDEBUG_DATABASE;
   MDatabaseInfo *db_info;
   char *filename;
   FILE *fp;
@@ -1030,7 +1011,7 @@ mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
   MDEBUG_PRINT1 (" [DB]  <%s>.\n",
                 gen_database_name (name, mdb->tag));
   db_info = mdb->extra_info;
-  filename = get_database_file (db_info, NULL);
+  filename = get_database_file (db_info, NULL, NULL);
   if (! filename || ! (fp = fopen (filename, "r")))
     MERROR (MERROR_DB, NULL);
   plist = mplist__from_file (fp, keys);
@@ -1054,22 +1035,17 @@ mdatabase__check (MDatabase *mdb)
 {
   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
   struct stat buf;
+  int result;
+
+  if (db_info->absolute_filename != db_info->filename
+      || db_info->status == MDB_STATUS_AUTO)
+    mdatabase__update ();
 
-  if (! get_database_file (db_info, &buf))
+  if (! get_database_file (db_info, &buf, &result)
+      || result < 0)
     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;
-    }
-
   return 1;
 }
 
@@ -1081,16 +1057,17 @@ char *
 mdatabase__find_file (char *filename)
 {
   struct stat buf;
+  int result;
   MDatabaseInfo db_info;
 
   if (filename[0] == PATH_SEPARATOR)
-    return (stat (filename, &buf) == 0 ? filename : NULL);
+    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)
-      || stat (db_info.absolute_filename, &buf) < 0)
+  if (! get_database_file (&db_info, &buf, &result)
+      || result < 0)
     return NULL;
   return db_info.absolute_filename;
 }
@@ -1103,7 +1080,7 @@ mdatabase__file (MDatabase *mdb)
   if (mdb->loader != load_database)
     return NULL;
   db_info = mdb->extra_info;
-  return get_database_file (db_info, NULL);
+  return get_database_file (db_info, NULL, NULL);
 }
 
 int
@@ -1120,7 +1097,7 @@ mdatabase__lock (MDatabase *mdb)
   db_info = mdb->extra_info;
   if (db_info->lock_file)
     return -1;
-  file = get_database_file (db_info, NULL);
+  file = get_database_file (db_info, NULL, NULL);
   if (! file)
     return -1;
   len = strlen (file);
@@ -1184,7 +1161,7 @@ mdatabase__save (MDatabase *mdb, MPlist *data)
   db_info = mdb->extra_info;
   if (! db_info->lock_file)
     return -1;
-  file = get_database_file (db_info, NULL);
+  file = get_database_file (db_info, NULL, NULL);
   if (! file)
     return -1;
   mt = mtext ();
@@ -1199,9 +1176,11 @@ mdatabase__save (MDatabase *mdb, MPlist *data)
       M17N_OBJECT_UNREF (mt);
       return -1;
     }
-  mconv_encode_stream (msymbol ("utf-8"), mt, fp);
-  M17N_OBJECT_UNREF (mt);
+  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);
+  M17N_OBJECT_UNREF (mt);
   if ((ret = rename (db_info->uniq_file, file)) < 0)
     unlink (db_info->uniq_file);
   free (db_info->uniq_file);
@@ -1230,12 +1209,45 @@ mdatabase__unlock (MDatabase *mdb)
   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 */
 
 \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 */
 /*** @{ */
 
@@ -1320,21 +1332,49 @@ mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
     {
       p0 = MPLIST_PLIST (p);
       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
-      if (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0)
+      if (MPLIST_SYMBOL (p0) == Masterisk
+         || (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0))
        continue;
       MPLIST_DO (p0, MPLIST_NEXT (p0))
        {
          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);
@@ -1344,10 +1384,7 @@ mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
        }
     }
   if (MPLIST_TAIL_P (plist))
-    {
-      M17N_OBJECT_UNREF (plist);
-      plist = NULL;
-    }
+    M17N_OBJECT_UNREF (plist);
   return plist;
 }
 
@@ -1407,7 +1444,7 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
   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);
+  mdb = register_database (tags, loader, extra_info, MDB_STATUS_EXPLICIT, NULL);
   return mdb;
 }