Update copyright years
[m17n/m17n-lib.git] / src / database.c
index 93cc5ff..ca33ec4 100644 (file)
@@ -1,5 +1,5 @@
 /* database.c -- database module.
 /* 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
 
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H15PRO112
 
@@ -17,7 +17,7 @@
 
    You should have received a copy of the GNU Lesser General Public
    License along with the m17n library; if not, write to the Free
 
    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
    02111-1307, USA.  */
 
 /***en
 #include <time.h>
 #include <libgen.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 "m17n-misc.h"
 #include "internal.h"
 #include "mtext.h"
 #include "character.h"
-#include "charset.h"
 #include "database.h"
 #include "database.h"
-#include "coding.h"
 #include "plist.h"
 
 /** The file containing a list of databases.  */
 #include "plist.h"
 
 /** The file containing a list of databases.  */
       path[dir_len + file_len] = '\0', 1))
 
 static MSymbol Masterisk;
       path[dir_len + file_len] = '\0', 1))
 
 static MSymbol Masterisk;
+static MSymbol Mversion;
 
 /** Structure for a data in the m17n database.  */
 
 
 /** 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 == '"')
          /* 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 ();
          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)
 {
 static char *
 gen_database_name (char *buf, MSymbol *tags)
 {
@@ -434,52 +353,48 @@ 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.  */
+/* 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 *
 
 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)
   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;
     }
   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;
     }
 
   return db_info->absolute_filename;
@@ -490,16 +405,31 @@ load_database (MSymbol *tags, void *extra_info)
 {
   MDatabaseInfo *db_info = extra_info;
   void *value;
 {
   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;
   FILE *fp;
+  int mdebug_flag = MDEBUG_DATABASE;
+  char buf[256];
 
 
+  MDEBUG_PRINT1 (" [DB] <%s>", gen_database_name (buf, tags));
   if (! filename || ! (fp = fopen (filename, "r")))
   if (! filename || ! (fp = fopen (filename, "r")))
-    MERROR (MERROR_DB, NULL);
+    {
+      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)
 
   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);
   else
     value = mplist__from_file (fp, NULL);
   fclose (fp);
@@ -522,39 +452,70 @@ get_dir_info (char *dirname)
   if (dirname)
     {
       int len = strlen (dirname);
   if (dirname)
     {
       int len = strlen (dirname);
-      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)
+
+      if (len + MDB_DIR_LEN < PATH_MAX)
        {
        {
-         dir_info->filename[len] = PATH_SEPARATOR;
-         dir_info->filename[++len] = '\0';
+         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;
        }
        }
-      dir_info->len = len;
-      dir_info->status = MDB_STATUS_AUTO;
+      else
+       dir_info->status = MDB_STATUS_DISABLED; 
     }
   else
     dir_info->status = MDB_STATUS_DISABLED;
   return dir_info;
 }
 
     }
   else
     dir_info->status = MDB_STATUS_DISABLED;
   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;
 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++)
     {
   
   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;
        return NULL;
-      plist = MPLIST_PLIST (plist);
+      plist = MPLIST_PLIST (pl);
       plist = MPLIST_NEXT (plist);
     }
       plist = MPLIST_NEXT (plist);
     }
-  return MPLIST_VAL (plist);
+  mdb = MPLIST_VAL (plist);
+  return mdb;
 }
 
 static void
 }
 
 static void
@@ -564,26 +525,86 @@ free_db_info (MDatabaseInfo *db_info)
   if (db_info->absolute_filename
       && db_info->filename != db_info->absolute_filename)
     free (db_info->absolute_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);
 }
 
   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;
+
+  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 *
 static MDatabase *
-register_database (MSymbol tags[4], void *(*loader) (MSymbol *, void *),
-                  void *extra_info, enum MDatabaseStatus status)
+register_database (MSymbol tags[4],
+                  void *(*loader) (MSymbol *, void *),
+                  void *extra_info, enum MDatabaseStatus status,
+                  MPlist *properties)
 {
 {
-  MDatabase *mdb = find_database (tags);
-  MDatabaseInfo *db_info = NULL;
+  MDatabase *mdb;
+  MDatabaseInfo *db_info;
+  int i;
+  MPlist *plist;
 
 
-  if (mdb)
+  if (properties)
     {
     {
-      if (loader == load_database)
-       db_info = mdb->extra_info;
+      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;
+             }
+         }
     }
     }
-  else
+
+  for (i = 0, plist = mdatabase__list; i < 4; i++)
     {
     {
-      int i;
-      MPlist *plist;
+      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];
       MSTRUCT_MALLOC (mdb, MERROR_DB);
       for (i = 0; i < 4; i++)
        mdb->tag[i] = tags[i];
@@ -594,26 +615,20 @@ register_database (MSymbol tags[4], void *(*loader) (MSymbol *, void *),
          mdb->extra_info = db_info;
        }
       else
          mdb->extra_info = db_info;
        }
       else
-       mdb->extra_info = extra_info;
-      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);
+         db_info = NULL;
+         mdb->extra_info = extra_info;
        }
       mplist_push (plist, Mt, mdb);
     }
        }
       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)
     {
 
   if (db_info)
     {
@@ -634,6 +649,12 @@ register_database (MSymbol tags[4], void *(*loader) (MSymbol *, void *),
        db_info->absolute_filename = db_info->filename;
       else
        db_info->absolute_filename = NULL;
        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
     }
 
   if (mdb->tag[0] == Mchar_table
@@ -646,57 +667,117 @@ register_database (MSymbol tags[4], void *(*loader) (MSymbol *, void *),
 }
 
 static void
 }
 
 static void
-register_databases_in_files (MSymbol tags[4], glob_t *globbuf)
+register_databases_in_files (MSymbol tags[4], char *filename, int len)
 {
   int i, j;
   MPlist *load_key = mplist ();
   FILE *fp;
 {
   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)
-           register_database (tags2, load_database, globbuf->gl_pathv[i], 1);
+         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);
        }
        }
-      M17N_OBJECT_UNREF (plist);
+      globfree (&globbuf);
+      if (filename[0] == PATH_SEPARATOR)
+       break;
     }
   M17N_OBJECT_UNREF (load_key);
 }
 
     }
   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;
 
 \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;
 
 int
 mdatabase__init ()
 {
   MDatabaseInfo *dir_info;
   char *path;
 
+  mdatabase__load_charset_func = NULL;
+
   Mchar_table = msymbol ("char-table");
   Mchar_table = msymbol ("char-table");
+  Mcharset = msymbol ("charset");
   Masterisk = msymbol ("*");
   Masterisk = msymbol ("*");
+  Mversion = msymbol ("version");
 
   mdatabase__dir_list = mplist ();
   /** The macro M17NDIR specifies a directory where the system-wide
 
   mdatabase__dir_list = mplist ();
   /** The macro M17NDIR specifies a directory where the system-wide
@@ -734,10 +815,6 @@ mdatabase__init ()
        mplist_push (mdatabase__dir_list, Mt, get_dir_info (NULL));
     }
 
        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;
   mdatabase__list = mplist ();
   mdatabase__update ();
   return 0;
@@ -789,6 +866,46 @@ mdatabase__update (void)
   char path[PATH_MAX + 1];
   MDatabaseInfo *dir_info;
   struct stat statbuf;
   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".  */
 
   /* At first, mark all databases defined automatically from mdb.dir
      file(s) as "disabled".  */
@@ -818,19 +935,13 @@ mdatabase__update (void)
        }
     }
 
        }
     }
 
-  /* Update elements of mdatabase__dir_list.  */
-  MPLIST_DO (plist, mdatabase__dir_list)
-    {
-      dir_info = MPLIST_VAL (plist);
-      dir_info->status = ((dir_info->filename
-                          && stat (dir_info->filename, &statbuf) == 0
-                          && (statbuf.st_mode & S_IFDIR))
-                         ? MDB_STATUS_AUTO : MDB_STATUS_DISABLED);
-    }
+  plist = mplist (); 
+  MPLIST_DO (p0, mdatabase__dir_list)
+    mplist_push (plist, MPLIST_KEY (p0), MPLIST_VAL (p0));
 
 
-  MPLIST_DO (plist, mdatabase__dir_list)
+  while (! MPLIST_TAIL_P (plist))
     {
     {
-      MDatabaseInfo *dir_info = MPLIST_VAL (plist);
+      MDatabaseInfo *dir_info = mplist_pop (plist);
       MPlist *pl, *p;
       int i;
       FILE *fp;
       MPlist *pl, *p;
       int i;
       FILE *fp;
@@ -840,11 +951,6 @@ mdatabase__update (void)
       if (! GEN_PATH (path, dir_info->filename, dir_info->len,
                      MDB_DIR, MDB_DIR_LEN))
        continue;
       if (! GEN_PATH (path, dir_info->filename, dir_info->len,
                      MDB_DIR, MDB_DIR_LEN))
        continue;
-      if (stat (path, &statbuf) < 0)
-       continue;
-      if (dir_info->time >= statbuf.st_mtime)
-       continue;
-      dir_info->time = statbuf.st_mtime;
       if (! (fp = fopen (path, "r")))
        continue;
       pl = mplist__from_file (fp, NULL);
       if (! (fp = fopen (path, "r")))
        continue;
       pl = mplist__from_file (fp, NULL);
@@ -869,58 +975,29 @@ mdatabase__update (void)
              || ! MPLIST_MTEXT_P (p1))
            continue;
          for (; i < 4; i++)
              || ! MPLIST_MTEXT_P (p1))
            continue;
          for (; i < 4; i++)
-           tags[i] = Mnil;
+           tags[i] = with_wildcard ? Masterisk : Mnil;
          mt = MPLIST_MTEXT (p1);
          mt = MPLIST_MTEXT (p1);
-         if (mt->format >= MTEXT_FORMAT_UTF_16LE)
-           mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
          nbytes = mtext_nbytes (mt);
          if (nbytes > PATH_MAX)
            continue;
          memcpy (path, MTEXT_DATA (mt), nbytes);
          path[nbytes] = '\0';
          if (with_wildcard)
          nbytes = mtext_nbytes (mt);
          if (nbytes > PATH_MAX)
            continue;
          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);
-                 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);
-                   globfree (&globbuf);
-                 }
-           }
+           register_database (tags, load_database, path,
+                              MDB_STATUS_AUTO_WILDCARD, NULL);
          else
          else
-           {
-             register_database (tags, load_database, path, 1);
-           }
+           register_database (tags, load_database, path,
+                              MDB_STATUS_AUTO, p1);
        }
       M17N_OBJECT_UNREF (pl);
     }
        }
       M17N_OBJECT_UNREF (pl);
     }
+  M17N_OBJECT_UNREF (plist);
 }
 
 MPlist *
 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
 {
 }
 
 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;
   MDatabaseInfo *db_info;
   char *filename;
   FILE *fp;
@@ -931,10 +1008,10 @@ mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
       || mdb->tag[0] == Mchar_table
       || mdb->tag[0] == Mcharset)
     MERROR (MERROR_DB, NULL);
       || mdb->tag[0] == Mchar_table
       || mdb->tag[0] == Mcharset)
     MERROR (MERROR_DB, NULL);
-  MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
+  MDEBUG_PRINT1 (" [DB]  <%s>.\n",
                 gen_database_name (name, mdb->tag));
   db_info = mdb->extra_info;
                 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);
   if (! filename || ! (fp = fopen (filename, "r")))
     MERROR (MERROR_DB, NULL);
   plist = mplist__from_file (fp, keys);
@@ -958,8 +1035,14 @@ mdatabase__check (MDatabase *mdb)
 {
   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
   struct stat buf;
 {
   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
   struct stat buf;
+  int result;
 
 
-  if (! get_database_file (db_info, &buf))
+  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;
   if (db_info->time < buf.st_mtime)
     return 0;
@@ -974,16 +1057,17 @@ char *
 mdatabase__find_file (char *filename)
 {
   struct stat buf;
 mdatabase__find_file (char *filename)
 {
   struct stat buf;
+  int result;
   MDatabaseInfo db_info;
 
   if (filename[0] == PATH_SEPARATOR)
   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;
   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;
 }
     return NULL;
   return db_info.absolute_filename;
 }
@@ -996,7 +1080,7 @@ mdatabase__file (MDatabase *mdb)
   if (mdb->loader != load_database)
     return NULL;
   db_info = mdb->extra_info;
   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
 }
 
 int
@@ -1013,7 +1097,7 @@ mdatabase__lock (MDatabase *mdb)
   db_info = mdb->extra_info;
   if (db_info->lock_file)
     return -1;
   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);
   if (! file)
     return -1;
   len = strlen (file);
@@ -1077,7 +1161,7 @@ mdatabase__save (MDatabase *mdb, MPlist *data)
   db_info = mdb->extra_info;
   if (! db_info->lock_file)
     return -1;
   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 ();
   if (! file)
     return -1;
   mt = mtext ();
@@ -1092,9 +1176,11 @@ mdatabase__save (MDatabase *mdb, MPlist *data)
       M17N_OBJECT_UNREF (mt);
       return -1;
     }
       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);
   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);
   if ((ret = rename (db_info->uniq_file, file)) < 0)
     unlink (db_info->uniq_file);
   free (db_info->uniq_file);
@@ -1123,12 +1209,45 @@ mdatabase__unlock (MDatabase *mdb)
   return 0;
 }
 
   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 */
 
 /*** @} */
 #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 */
 /*** @{ */
 
 /*** @addtogroup m17nDatabase */
 /*** @{ */
 
@@ -1213,21 +1332,49 @@ mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
     {
       p0 = MPLIST_PLIST (p);
       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
     {
       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);
        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 (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 (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);
                  if (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3)
                    continue;
                  p3 = MPLIST_NEXT (p3);
@@ -1237,10 +1384,7 @@ mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
        }
     }
   if (MPLIST_TAIL_P (plist))
        }
     }
   if (MPLIST_TAIL_P (plist))
-    {
-      M17N_OBJECT_UNREF (plist);
-      plist = NULL;
-    }
+    M17N_OBJECT_UNREF (plist);
   return plist;
 }
 
   return plist;
 }
 
@@ -1300,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;
   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
   if (! loader)
     loader = load_database;
-  mdb = register_database (tags, loader, extra_info, 0);
+  mdb = register_database (tags, loader, extra_info, MDB_STATUS_EXPLICIT, NULL);
   return mdb;
 }
 
   return mdb;
 }
 
@@ -1361,11 +1505,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);
 }