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.
@brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤È¤½¤ì¤Ë´Ø¤¹¤ë API.
m17n ¥é¥¤¥Ö¥é¥ê¤ÏɬÍפ˱þ¤¸¤ÆưŪ¤Ë @e m17n @e ¥Ç¡¼¥¿¥Ù¡¼¥¹
- ¤«¤é¾ðÊó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤âÆȼ«¤Î¥Ç¡¼¥¿¤ò
- m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤¤ë¡£m17n
+ ¤«¤é¾ðÊó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤â¡¢Æȼ«¤Î¥Ç¡¼¥¿¤ò
+ m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤¤ë¡£
+ ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬Æȼ«¤Î¥Ç¡¼¥¿¤òÄɲᦼèÆÀ¤¹¤ë¤Ë¤Ï¡¢ÊÑ¿ô
+ #mdatabase_dir ¤Ë¤½¤Î¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇͤΥǥ£¥ì¥¯¥È¥ê¤ò¥»¥Ã¥È¤·¡¢
+ ¤½¤ÎÃæ¤Ë¥Ç¡¼¥¿¤ò³ÊǼ¤¹¤ë¡£¥æ¡¼¥¶¤¬¤½¤Î¥Ç¡¼¥¿¤ò¥ª¡¼¥Ð¡¼¥é¥¤¥È¤·¤¿¤¤
+ ¤È¤¤Ï¡¢´Ä¶ÊÑ¿ô "M17NDIR" ¤Ç»ØÄꤵ¤ì¤ë¥Ç¥£¥ì¥¯¥È¥ê¡Ê»ØÄꤵ¤ì¤Æ¤¤¤Ê
+ ¤¤¤È¤¤Ï "~/.m17n.d" ¤È¤¤¤¦¥Ç¥£¥ì¥¯¥È¥ê¡Ë¤ËÊ̤Υǡ¼¥¿¤òÃÖ¤¯¡£
+
+ m17n
¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤ÏÊ£¿ô¤Î¿Íͤʥǡ¼¥¿¤¬´Þ¤Þ¤ì¤Æ¤ª¤ê¡¢³Æ¥Ç¡¼¥¿¤Ï
TAG0, TAG1, TAG2, TAG3¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Î£´¤Ä¤Î¥¿¥°¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
+#include <glob.h>
+#include <time.h>
+#include <libgen.h>
#include "m17n.h"
#include "m17n-misc.h"
/** 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;
/** Structure for a data in the m17n database. */
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)
{
char *p = buf + i;
- while (! isspace (*p))
+ while (*p && ! isspace (*p))
{
- if (*p == '\\' || p[1] != '\0')
+ if (*p == '\\' && p[1] != '\0')
{
memmove (p, p + 1, buf + len - (p + 1));
len--;
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)
+char *
+find_file (MDatabaseInfo *db_info, struct stat *buf)
{
- FILE *fp = NULL;
+ 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. */
- if (filename[0] == '/')
- fp = fopen (filename, "r");
+char *
+get_database_file (MDatabaseInfo *db_info, struct stat *buf)
+{
+ if (db_info->status == MDB_STATUS_DISABLED)
+ return NULL;
+ if (db_info->absolute_filename)
+ {
+ if (buf)
+ stat (db_info->absolute_filename, buf);
+ }
else
{
- MPlist *plist;
- char path[PATH_MAX];
+ struct stat stat_buf;
+ struct stat *statbuf = buf ? buf : &stat_buf;
- MPLIST_DO (plist, mdatabase__dir_list)
- {
- strcpy (path, (char *) MPLIST_VAL (plist));
- strcat (path, filename);
- fp = fopen (path, "r");
- if (fp)
- break;
- }
+ db_info->absolute_filename = find_file (db_info, statbuf);
}
- 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);
+ FILE *fp;
+ int mdebug_mask = 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]);
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 (! dirname
- || stat (dirname, &buf) < 0)
+ 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 MDatabase *
+find_database (MSymbol tags[4])
+{
+ MPlist *plist;
+ int i;
+
+ if (! mdatabase__list)
return NULL;
+ for (i = 0, plist = mdatabase__list; i < 4; i++)
+ {
+ plist = mplist__assq (plist, tags[i]);
+ if (! plist)
+ return NULL;
+ plist = MPLIST_PLIST (plist);
+ plist = MPLIST_NEXT (plist);
+ }
+ return MPLIST_VAL (plist);
+}
+
+static void
+free_db_info (MDatabaseInfo *db_info)
+{
+ free (db_info->filename);
+ if (db_info->absolute_filename
+ && db_info->filename != db_info->absolute_filename)
+ free (db_info->absolute_filename);
+ free (db_info);
+}
+
+static int
+check_version (MText *version)
+{
+ char *verstr = (char *) MTEXT_DATA (version);
+ char *endp = verstr + mtext_nbytes (version);
+ int ver[3];
+ int i;
+
+ 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)
+{
+ MDatabase *mdb;
+ MDatabaseInfo *db_info;
+ int i;
+ MPlist *plist;
+
+ if (! mdatabase__list)
+ mdatabase__list = mplist ();
+
+ for (i = 0, plist = mdatabase__list; i < 4; i++)
+ {
+ MPlist *pl = mplist__assq (plist, tags[i]);
+
+ if (pl)
+ pl = MPLIST_PLIST (pl);
+ else
+ {
+ pl = mplist ();
+ mplist_add (pl, Msymbol, tags[i]);
+ mplist_push (plist, Mplist, pl);
+ M17N_OBJECT_UNREF (pl);
+ }
+ plist = MPLIST_NEXT (pl);
+ }
+
+ if (MPLIST_TAIL_P (plist))
+ {
+ MSTRUCT_MALLOC (mdb, MERROR_DB);
+ for (i = 0; i < 4; i++)
+ mdb->tag[i] = tags[i];
+ mdb->loader = loader;
+ if (loader == load_database)
+ {
+ MSTRUCT_CALLOC (db_info, MERROR_DB);
+ mdb->extra_info = db_info;
+ }
+ else
+ {
+ db_info = NULL;
+ mdb->extra_info = extra_info;
+ }
+ mplist_push (plist, Mt, mdb);
+ }
+ else
+ {
+ mdb = MPLIST_VAL (plist);
+ if (loader == load_database)
+ db_info = mdb->extra_info;
+ else
+ db_info = NULL;
+ }
+
+ if (db_info)
+ {
+ db_info->status = status;
+ if (! db_info->filename
+ || strcmp (db_info->filename, (char *) extra_info) != 0)
+ {
+ if (db_info->filename)
+ free (db_info->filename);
+ if (db_info->absolute_filename
+ && db_info->filename != db_info->absolute_filename)
+ free (db_info->absolute_filename);
+ db_info->filename = strdup ((char *) extra_info);
+ db_info->len = strlen ((char *) extra_info);
+ db_info->time = 0;
+ }
+ if (db_info->filename[0] == PATH_SEPARATOR)
+ db_info->absolute_filename = db_info->filename;
+ else
+ db_info->absolute_filename = NULL;
+ }
+
+ if (mdb->tag[0] == Mchar_table
+ && mdb->tag[2] != Mnil
+ && (mdb->tag[1] == Mstring || mdb->tag[1] == Mtext
+ || mdb->tag[1] == Msymbol || mdb->tag[1] == Minteger
+ || mdb->tag[1] == Mplist))
+ mchar__define_prop (mdb->tag[2], mdb->tag[1], mdb);
+ return mdb;
+}
+
+static void
+register_databases_in_files (MSymbol tags[4], glob_t *globbuf, int headlen)
+{
+ int i, j;
+ MPlist *load_key = mplist ();
+ FILE *fp;
+ MPlist *plist;
- len = strlen (dirname);
- MTABLE_MALLOC (str, len + 2, MERROR_DB);
- memcpy (str, dirname, len + 1);
- if (str[len - 1] != '/')
+ for (i = 0; i < globbuf->gl_pathc; i++)
{
- str[len] = '/';
- str[len + 1] = '\0';
+ if (! (fp = fopen (globbuf->gl_pathv[i], "r")))
+ continue;
+ plist = mplist__from_file (fp, load_key);
+ fclose (fp);
+ if (! plist)
+ continue;
+ if (MPLIST_PLIST_P (plist))
+ {
+ MPlist *pl;
+ MSymbol tags2[4];
+
+ for (j = 0, pl = MPLIST_PLIST (plist); j < 4 && MPLIST_SYMBOL_P (pl);
+ j++, pl = MPLIST_NEXT (pl))
+ tags2[j] = MPLIST_SYMBOL (pl);
+ for (; j < 4; j++)
+ tags2[j] = Mnil;
+ for (j = 0; j < 4; j++)
+ if (tags[j] == Masterisk ? tags2[j] == Mnil
+ : (tags[j] != Mnil && tags[j] != tags2[j]))
+ break;
+ if (j == 4)
+ {
+ MText *version = NULL;
+
+ MPLIST_DO (pl, pl)
+ version = MPLIST_MTEXT_P (pl) ? MPLIST_MTEXT (pl) : NULL;
+ if (! version || check_version (version))
+ register_database (tags2, load_database,
+ globbuf->gl_pathv[i] + headlen,
+ MDB_STATUS_AUTO);
+ }
+ }
+ M17N_OBJECT_UNREF (plist);
}
- return str;
+ M17N_OBJECT_UNREF (load_key);
}
\f
/** List of database directories. */
MPlist *mdatabase__dir_list;
-MSymbol M_database_hook;
-
int
mdatabase__init ()
{
- char *dir;
- int i;
- MPlist *plist;
- FILE *fp;
+ MDatabaseInfo *dir_info;
+ char *path;
Mchar_table = msymbol ("char-table");
- M_database_hook = msymbol (" database-hook");
+ Masterisk = msymbol ("*");
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;
+
+ if (home
+ && (len = strlen (home))
+ && (path = alloca (len + 9)))
+ {
+ strcpy (path, home);
+ if (path[len - 1] != PATH_SEPARATOR)
+ path[len++] = PATH_SEPARATOR;
+ strcpy (path + len, ".m17n.d");
+ dir_info = get_dir_info (path);
+ mplist_push (mdatabase__dir_list, Mt, dir_info);
+ }
+ else
+ mplist_push (mdatabase__dir_list, Mt, get_dir_info (NULL));
+ }
+
+ mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
+ mdatabase_find);
+ mdatabase__loader = (void *(*) (void *)) mdatabase_load;
- /* 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);
+ mdatabase__list = mplist ();
+ mdatabase__update ();
+ return 0;
+}
+
+void
+mdatabase__fini (void)
+{
+ MPlist *plist, *p0, *p1, *p2, *p3;
- MLIST_INIT1 (&mdb_list, mdbs, 256);
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);
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] = Mnil;
+ mt = MPLIST_MTEXT (p1);
+ 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;
- mdb.loader = load_database;
- nbytes = mconv_encode_buffer (Mcoding_utf_8, MPLIST_MTEXT (p1),
- (unsigned char *) path, PATH_MAX);
- if (nbytes < 0 || nbytes >= PATH_MAX)
- continue;
- path[nbytes++] = '\0';
- mdb.extra_info = (void *) strdup (path);
- MLIST_APPEND1 (&mdb_list, mdbs, mdb, MERROR_DB);
+ if (tags[0] == Mchar_table || tags[0] == Mcharset)
+ continue;
+ if (path[0] == PATH_SEPARATOR)
+ {
+ if (glob (path, GLOB_NOSORT, NULL, &globbuf))
+ continue;
+ register_databases_in_files (tags, &globbuf, 0);
+ globfree (&globbuf);
+ }
+ else
+ MPLIST_DO (dlist, mdatabase__dir_list)
+ {
+ MDatabaseInfo *d_info = MPLIST_VAL (dlist);
+
+ if (d_info->status == MDB_STATUS_DISABLED)
+ continue;
+ if (! GEN_PATH (path, d_info->filename, d_info->len,
+ MTEXT_DATA (mt), nbytes))
+ continue;
+ if (glob (path, GLOB_NOSORT, NULL, &globbuf))
+ continue;
+ register_databases_in_files (tags, &globbuf, d_info->len);
+ globfree (&globbuf);
+ }
+ }
+ else
+ {
+ MText *version = NULL;
+
+ MPLIST_DO (p1, p1)
+ version = MPLIST_MTEXT_P (pl) ? MPLIST_MTEXT (pl) : NULL;
+ if (! version || check_version (version))
+ register_database (tags, load_database, path, MDB_STATUS_AUTO);
+ }
}
M17N_OBJECT_UNREF (pl);
}
-
- mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
- mdatabase_find);
- mdatabase__loader = (void *(*) (void *)) mdatabase_load;
-
- return 0;
-}
-
-void
-mdatabase__fini (void)
-{
- int i;
- MPlist *plist;
-
- MPLIST_DO (plist, mdatabase__dir_list)
- free (MPLIST_VAL (plist));
- M17N_OBJECT_UNREF (mdatabase__dir_list);
-
- for (i = 0; i < mdb_list.used; i++)
- {
- MDatabase *mdb = mdb_list.mdbs + i;
-
- if (mdb->loader == load_database)
- free (mdb->extra_info);
- }
- MLIST_FREE1 (&mdb_list, mdbs);
+ M17N_OBJECT_UNREF (plist);
}
MPlist *
mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
{
int mdebug_mask = MDEBUG_DATABASE;
+ MDatabaseInfo *db_info;
+ char *filename;
FILE *fp;
MPlist *plist;
- char buf[256];
+ char name[256];
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 (! fp)
+ MDEBUG_PRINT1 (" [DB] <%s>.\n",
+ gen_database_name (name, mdb->tag));
+ db_info = mdb->extra_info;
+ filename = get_database_file (db_info, NULL);
+ if (! filename || ! (fp = fopen (filename, "r")))
MERROR (MERROR_DB, NULL);
plist = mplist__from_file (fp, keys);
fclose (fp);
}
+/* 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)
+{
+ MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
+ struct stat buf;
+
+ if (! get_database_file (db_info, &buf))
+ return -1;
+ if (db_info->time < buf.st_mtime)
+ return 0;
+ if (db_info->status == MDB_STATUS_AUTO
+ && db_info->filename != db_info->absolute_filename)
+ {
+ MDatabase *new;
+
+ mdatabase__update ();
+ new = find_database (mdb->tag);
+ if (new != mdb)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Search directories in mdatabase__dir_list for file FILENAME. If
+ the file exist, return the absolute pathname. If FILENAME is
+ already absolute, return a copy of it. */
+
+char *
+mdatabase__find_file (char *filename)
+{
+ struct stat buf;
+ MDatabaseInfo db_info;
+
+ if (filename[0] == PATH_SEPARATOR)
+ return (stat (filename, &buf) == 0 ? filename : NULL);
+ db_info.filename = filename;
+ db_info.len = strlen (filename);
+ db_info.time = 0;
+ db_info.absolute_filename = NULL;
+ if (! get_database_file (&db_info, &buf)
+ || stat (db_info.absolute_filename, &buf) < 0)
+ return NULL;
+ return db_info.absolute_filename;
+}
+
+char *
+mdatabase__file (MDatabase *mdb)
+{
+ MDatabaseInfo *db_info;
+
+ if (mdb->loader != load_database)
+ return NULL;
+ db_info = mdb->extra_info;
+ return get_database_file (db_info, NULL);
+}
+
+int
+mdatabase__lock (MDatabase *mdb)
+{
+ MDatabaseInfo *db_info;
+ struct stat buf;
+ FILE *fp;
+ int len;
+ char *file;
+
+ if (mdb->loader != load_database)
+ return -1;
+ db_info = mdb->extra_info;
+ if (db_info->lock_file)
+ return -1;
+ file = get_database_file (db_info, NULL);
+ if (! file)
+ return -1;
+ len = strlen (file);
+ db_info->uniq_file = malloc (len + 35);
+ if (! db_info->uniq_file)
+ return -1;
+ db_info->lock_file = malloc (len + 5);
+ if (! db_info->lock_file)
+ {
+ free (db_info->uniq_file);
+ return -1;
+ }
+ sprintf (db_info->uniq_file, "%s.%X.%X", db_info->absolute_filename,
+ (unsigned) time (NULL), (unsigned) getpid ());
+ sprintf (db_info->lock_file, "%s.LCK", db_info->absolute_filename);
+
+ fp = fopen (db_info->uniq_file, "w");
+ if (! fp)
+ {
+ char *str = strdup (db_info->uniq_file);
+ char *dir = dirname (str);
+
+ if (stat (dir, &buf) == 0
+ || mkdir (dir, 0777) < 0
+ || ! (fp = fopen (db_info->uniq_file, "w")))
+ {
+ free (db_info->uniq_file);
+ free (db_info->lock_file);
+ db_info->lock_file = NULL;
+ free (str);
+ return -1;
+ }
+ free (str);
+ }
+ fclose (fp);
+ if (link (db_info->uniq_file, db_info->lock_file) < 0
+ && (stat (db_info->uniq_file, &buf) < 0
+ || buf.st_nlink != 2))
+ {
+ unlink (db_info->uniq_file);
+ unlink (db_info->lock_file);
+ free (db_info->uniq_file);
+ free (db_info->lock_file);
+ db_info->lock_file = NULL;
+ return 0;
+ }
+ return 1;
+}
+
+int
+mdatabase__save (MDatabase *mdb, MPlist *data)
+{
+ MDatabaseInfo *db_info;
+ FILE *fp;
+ char *file;
+ MText *mt;
+ int ret;
+
+ if (mdb->loader != load_database)
+ return -1;
+ db_info = mdb->extra_info;
+ if (! db_info->lock_file)
+ return -1;
+ file = get_database_file (db_info, NULL);
+ if (! file)
+ return -1;
+ mt = mtext ();
+ if (mplist__serialize (mt, data, 1) < 0)
+ {
+ M17N_OBJECT_UNREF (mt);
+ return -1;
+ }
+ fp = fopen (db_info->uniq_file, "w");
+ if (! fp)
+ {
+ M17N_OBJECT_UNREF (mt);
+ return -1;
+ }
+ mconv_encode_stream (msymbol ("utf-8"), mt, fp);
+ M17N_OBJECT_UNREF (mt);
+ fclose (fp);
+ if ((ret = rename (db_info->uniq_file, file)) < 0)
+ unlink (db_info->uniq_file);
+ free (db_info->uniq_file);
+ db_info->uniq_file = NULL;
+ return ret;
+}
+
+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;
+}
+
/*** @} */
#endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
MDatabase *
mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
{
- int i;
- MDatabaseHookFunc func
- = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook);
+ MSymbol tags[4];
- if (func)
- func (tag0, tag1, tag2, tag3);
-
- for (i = 0; i < mdb_list.used; i++)
- {
- MDatabase *mdb = mdb_list.mdbs + i;
-
- 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);
}
/*=*/
¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
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 (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0)
+ continue;
+ MPLIST_DO (p0, MPLIST_NEXT (p0))
{
- if (! plist)
- plist = pl = mplist ();
- pl = mplist_add (pl, Mt, mdb);
+ p1 = MPLIST_PLIST (p0);
+ if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1)
+ continue;
+ MPLIST_DO (p1, MPLIST_NEXT (p1))
+ {
+ p2 = MPLIST_PLIST (p1);
+ if (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2)
+ continue;
+ MPLIST_DO (p2, MPLIST_NEXT (p2))
+ {
+ p3 = MPLIST_PLIST (p2);
+ if (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3)
+ continue;
+ p3 = MPLIST_NEXT (p3);
+ pl = mplist_add (pl, Mt, MPLIST_VAL (p3));
+ }
+ }
}
}
+ if (MPLIST_TAIL_P (plist))
+ {
+ M17N_OBJECT_UNREF (plist);
+ plist = NULL;
+ }
return plist;
}
-
-
/*=*/
/***en
@brief Define a data of the m17n database.
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);
+ return 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);
}