X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=src%2Fdatabase.c;h=0f7891c16fd35b4fa1747e77956ac15ddbfcc75a;hb=ef71335ceb286b3669919046ebccfbdf8cea9819;hp=edadcab67609f61a05a52f73b525716bdd51b952;hpb=6cd6b846e111e87816fc15e58893a1991638bf17;p=m17n%2Fm17n-lib.git diff --git a/src/database.c b/src/database.c index edadcab..0f7891c 100644 --- a/src/database.c +++ b/src/database.c @@ -17,21 +17,21 @@ 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 m17n database. Application + The m17n library acquires various kinds of information + from data in the m17n database on demand. Application programs can also add/load their original data to/from the m17n database by setting the variable #mdatabase_dir to an application-specific directory and storing data in it. Users can - overwrite those data by storing overwriting-data in the directory - "~/.m17n.d" or in a directory specified by the environment - variable "M17NDIR". + 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 @@ -74,8 +74,15 @@ @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤È¤½¤ì¤Ë´Ø¤¹¤ë API. m17n ¥é¥¤¥Ö¥é¥ê¤ÏɬÍפ˱þ¤¸¤ÆưŪ¤Ë @e m17n @e ¥Ç¡¼¥¿¥Ù¡¼¥¹ - ¤«¤é¾ðÊó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤âÆȼ«¤Î¥Ç¡¼¥¿¤ò - m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£m17n + ¤«¤é¾ðÊó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤â¡¢Æȼ«¤Î¥Ç¡¼¥¿¤ò + m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£ + ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬Æȼ«¤Î¥Ç¡¼¥¿¤òÄɲᦼèÆÀ¤¹¤ë¤Ë¤Ï¡¢ÊÑ¿ô + #mdatabase_dir ¤Ë¤½¤Î¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¥£¥ì¥¯¥È¥ê¤ò¥»¥Ã¥È¤·¡¢ + ¤½¤ÎÃæ¤Ë¥Ç¡¼¥¿¤ò³ÊǼ¤¹¤ë¡£¥æ¡¼¥¶¤¬¤½¤Î¥Ç¡¼¥¿¤ò¥ª¡¼¥Ð¡¼¥é¥¤¥È¤·¤¿¤¤ + ¤È¤­¤Ï¡¢´Ä¶­ÊÑ¿ô "M17NDIR" ¤Ç»ØÄꤵ¤ì¤ë¥Ç¥£¥ì¥¯¥È¥ê¡Ê»ØÄꤵ¤ì¤Æ¤¤¤Ê + ¤¤¤È¤­¤Ï "~/.m17n.d" ¤È¤¤¤¦¥Ç¥£¥ì¥¯¥È¥ê¡Ë¤ËÊ̤Υǡ¼¥¿¤òÃÖ¤¯¡£ + + m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤ÏÊ£¿ô¤Î¿Íͤʥǡ¼¥¿¤¬´Þ¤Þ¤ì¤Æ¤ª¤ê¡¢³Æ¥Ç¡¼¥¿¤Ï TAG0, TAG1, TAG2, TAG3¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Î£´¤Ä¤Î¥¿¥°¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£ @@ -126,6 +133,8 @@ #include #include #include +#include +#include #include "m17n.h" #include "m17n-misc.h" @@ -139,41 +148,16 @@ /** 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)) -#ifndef PATH_MAX -#define PATH_MAX 1024 -#endif - -#define PATH_SEPARATOR '/' - -#define USE_GEN_PATH \ - char _path_static[PATH_MAX], *_path = _path_static; \ - int _path_len = PATH_MAX, _this_len - -#define GEN_PATH(DIR, FILE) \ - (_this_len = strlen (DIR) + strlen (FILE) + 2, \ - sprintf (((_this_len > _path_len) \ - ? (_path_len = _this_len, _path = alloca (_path_len)) : _path), \ - "%s%c%s", (DIR), PATH_SEPARATOR, (FILE)), \ - _path) - -#define GEN_PATH_FROM_MT(DIR, MT) \ - (mtext_ref_char ((MT), 0) == PATH_SEPARATOR \ - ? (_this_len = mtext_nbytes (MT) + 1, \ - (_this_len > _path_len \ - ? (_path_len = _this_len, _path = alloca (_path_len)) \ - : _path)[_this_len - 1] = '\0', \ - memcpy (_path, MTEXT_DATA (MT), mtext_nbytes (MT))) \ - : (_this_len = strlen (DIR) + mtext_nbytes (MT) + 2, \ - (_this_len > _path_len \ - ? (_path_len = _this_len, _path = alloca (_path_len)) \ - : _path)[_this_len - 1] = '\0', \ - memcpy (_path + sprintf (_path, "%s%c", (DIR), PATH_SEPARATOR), \ - MTEXT_DATA (MT), mtext_nbytes (MT)), \ - _path)) +#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; @@ -195,21 +179,7 @@ struct MDatabase void *extra_info; }; -typedef struct -{ - char *filename; - time_t time; -} MDatabaseInfo; - -/** 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) @@ -464,57 +434,78 @@ 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 char * -get_database_filename (MDatabaseInfo *db_info) +char * +find_file (MDatabaseInfo *db_info, struct stat *buf) { - char *filename = NULL; - struct stat buf; + MPlist *plist; + char path[PATH_MAX + 1]; - if (db_info->filename[0] == '/') + MPLIST_DO (plist, mdatabase__dir_list) { - if (stat (db_info->filename, &buf) == 0) - { - filename = db_info->filename; - db_info->time = MAX_TIME (buf.st_mtime, buf.st_ctime); - } + MDatabaseInfo *dir_info = MPLIST_VAL (plist); + + if (dir_info->status != MDB_STATUS_DISABLED + && GEN_PATH (path, dir_info->filename, dir_info->len, + db_info->filename, db_info->len) + && stat (path, buf) == 0) + return strdup (path); + } + return NULL; +} + + +/* Return the absolute file name for DB_INFO->filename. If BUF is + non-NULL, store the result of `stat' call in it. It returns NULL + if no absolute file name was found. */ + +char * +get_database_file (MDatabaseInfo *db_info, struct stat *buf) +{ + if (db_info->status == MDB_STATUS_DISABLED) + return NULL; + if (db_info->absolute_filename) + { + if (buf) + stat (db_info->absolute_filename, buf); } else { - MPlist *plist; - USE_GEN_PATH; - - MPLIST_DO (plist, mdatabase__dir_list) - { - MDatabaseInfo *dir_info = MPLIST_VAL (plist); - char *path = GEN_PATH (dir_info->filename, db_info->filename); + struct stat stat_buf; + struct stat *statbuf = buf ? buf : &stat_buf; - if (stat (path, &buf) == 0) - { - free (db_info->filename); - filename = db_info->filename = strdup (path); - db_info->time = MAX_TIME (buf.st_mtime, buf.st_ctime); - break; - } - } + db_info->absolute_filename = find_file (db_info, statbuf); } - return filename; + + return db_info->absolute_filename; } static void * load_database (MSymbol *tags, void *extra_info) { - char *filename = get_database_filename ((MDatabaseInfo *) extra_info); - FILE *fp; + 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]; + MDEBUG_PRINT1 (" [DB] <%s>", gen_database_name (buf, tags)); 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]); @@ -526,202 +517,233 @@ load_database (MSymbol *tags, void *extra_info) if (! value) MERROR (MERROR_DB, NULL); + db_info->time = time (NULL); return value; } -/** If DIRNAME is a readable directory, allocate MDatabaseInfo and - 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 MDatabaseInfo * get_dir_info (char *dirname) { - struct stat buf; - int len; MDatabaseInfo *dir_info; - if (! dirname - || stat (dirname, &buf) < 0 - || ! (buf.st_mode & S_IFDIR)) - return NULL; + MSTRUCT_CALLOC (dir_info, MERROR_DB); + if (dirname) + { + int len = strlen (dirname); - MSTRUCT_MALLOC (dir_info, MERROR_DB); - len = strlen (dirname) + 1; - MTABLE_MALLOC (dir_info->filename, len, MERROR_DB); - memcpy (dir_info->filename, dirname, len); - /* Don't include the last '/' in filename. */ - if (dir_info->filename[len - 2] == PATH_SEPARATOR) - dir_info->filename[len - 2] = '\0'; - /* Set this to zero so that the first call of update_database_list - surely checks this directory. */ - dir_info->time = 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 tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) +find_database (MSymbol tags[4]) { + MPlist *plist; int i; - - for (i = 0; i < mdb_list.used; i++) + + if (! mdatabase__list) + return NULL; + for (i = 0, plist = mdatabase__list; i < 4; 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; + plist = mplist__assq (plist, tags[i]); + if (! plist) + return NULL; + plist = MPLIST_PLIST (plist); + plist = MPLIST_NEXT (plist); } - return NULL; + return MPLIST_VAL (plist); } static void -register_database (MDatabase *mdb, char *path) +free_db_info (MDatabaseInfo *db_info) { - if (find_database (mdb->tag[0], mdb->tag[1], mdb->tag[2], mdb->tag[3])) - return; - mdb->loader = load_database; - mdb->extra_info = calloc (1, sizeof (MDatabaseInfo)); - if (! mdb->extra_info) - MEMORY_FULL (MERROR_DB); - ((MDatabaseInfo*) mdb->extra_info)->filename = strdup (path); - MLIST_APPEND1 (&mdb_list, mdbs, *mdb, MERROR_DB); + 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_list.mdbs + mdb_list.used - 1); + mchar__define_prop (mdb->tag[2], mdb->tag[1], mdb); + return mdb; } static void -update_database_list () +register_databases_in_files (MSymbol tags[4], glob_t *globbuf, int headlen) { + int i, j; + MPlist *load_key = mplist (); + FILE *fp; MPlist *plist; - char *path, *file; - USE_GEN_PATH; - MPLIST_DO (plist, mdatabase__dir_list) + for (i = 0; i < globbuf->gl_pathc; i++) { - MDatabaseInfo *dir_info = MPLIST_VAL (plist); - struct stat statbuf; - MPlist *pl, *p; - int i, j, len; - FILE *fp; - char *file; - - if (stat (dir_info->filename, &statbuf) < 0) - continue; - if (dir_info->time >= statbuf.st_ctime - && dir_info->time >= statbuf.st_mtime) + if (! (fp = fopen (globbuf->gl_pathv[i], "r"))) continue; - dir_info->time = MAX_TIME (statbuf.st_ctime, statbuf.st_mtime); - path = GEN_PATH (dir_info->filename, MDB_DIR); - if (! (fp = fopen (path, "r"))) - continue; - pl = mplist__from_file (fp, NULL); + plist = mplist__from_file (fp, load_key); fclose (fp); - if (! pl) + if (! plist) continue; - MPLIST_DO (p, pl) + if (MPLIST_PLIST_P (plist)) { - MDatabase mdb; - MPlist *p1; - MText *mt; - int nbytes; - int with_wildcard = 0; - - if (! MPLIST_PLIST_P (p)) - continue; - p1 = MPLIST_PLIST (p); - if (! MPLIST_SYMBOL_P (p1)) - continue; - for (i = 0, p1 = MPLIST_PLIST (p); - i < 4 && MPLIST_KEY (p1) == Msymbol; - i++, p1 = MPLIST_NEXT (p1)) - with_wildcard |= ((mdb.tag[i] = MPLIST_SYMBOL (p1)) == Masterisk); - if (i == 0 - || mdb.tag[0] == Masterisk - || ! MPLIST_MTEXT_P (p1)) - continue; - for (; i < 4; i++) - mdb.tag[i] = Mnil; - mdb.loader = load_database; - mt = MPLIST_MTEXT (p1); - if (mt->format >= MTEXT_FORMAT_UTF_16LE) - mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8); - nbytes = mtext_nbytes (mt); -#ifdef PATH_MAX - if (nbytes > PATH_MAX) - continue; -#endif /* PATH_MAX */ - if (with_wildcard - && mdb.tag[0] != Mchar_table - && mdb.tag[0] != Mcharset) - { - MPlist *dlist; - MPlist *load_key = mplist (); - - MPLIST_DO (dlist, mdatabase__dir_list) - { - MDatabaseInfo *d_info = MPLIST_VAL (dlist); - glob_t globbuf; - - path = GEN_PATH_FROM_MT (d_info->filename, mt); - if (glob (path, GLOB_NOSORT | GLOB_NOCHECK, NULL, &globbuf)) - { - if (path[0] == PATH_SEPARATOR) - break; - continue; - } - for (i = 0; i < globbuf.gl_pathc; i++) - { - if (! (fp = fopen (globbuf.gl_pathv[i], "r"))) - continue; - p1 = mplist__from_file (fp, load_key); - fclose (fp); - if (! p1) - continue; - if (MPLIST_PLIST_P (p1)) - { - MPlist *p0; - MDatabase mdb2; - - for (j = 0, p0 = MPLIST_PLIST (p1); - j < 4 && MPLIST_SYMBOL_P (p0); - j++, p0 = MPLIST_NEXT (p0)) - mdb2.tag[j] = MPLIST_SYMBOL (p0); - for (; j < 4; j++) - mdb2.tag[j] = Mnil; - for (j = 0; j < 4; j++) - if (mdb.tag[j] == Masterisk - ? mdb2.tag[j] == Mnil - : (mdb.tag[j] != Mnil - && mdb.tag[j] != mdb2.tag[j])) - break; - if (j == 4) - register_database (&mdb2, globbuf.gl_pathv[i]); - } - M17N_OBJECT_UNREF (p1); - } - globfree (&globbuf); - if (MTEXT_DATA (mt)[0] == PATH_SEPARATOR) - break; - } - - M17N_OBJECT_UNREF (load_key); - } - else + 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) { - path = GEN_PATH_FROM_MT (dir_info->filename, mt); - register_database (&mdb, path); + 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 (pl); + M17N_OBJECT_UNREF (plist); } + M17N_OBJECT_UNREF (load_key); } @@ -734,6 +756,7 @@ int mdatabase__init () { MDatabaseInfo *dir_info; + char *path; Mchar_table = msymbol ("char-table"); Masterisk = msymbol ("*"); @@ -741,85 +764,273 @@ mdatabase__init () mdatabase__dir_list = mplist (); /** The macro M17NDIR specifies a directory where the system-wide MDB_DIR file exists. */ - if ((dir_info = get_dir_info (M17NDIR))) - mplist_set (mdatabase__dir_list, Mt, dir_info); + 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_info = get_dir_info (mdatabase_dir))) - mplist_push (mdatabase__dir_list, Mt, dir_info); - - /* The environment variable M17NDIR (if non-NULL) specifies a - directory where a user specific MDB_DIR file exists. */ - if ((dir_info = get_dir_info (getenv ("M17NDIR")))) - mplist_push (mdatabase__dir_list, Mt, dir_info); - /* If M17NDIR is not set, check "~/.m17n.d". */ + 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"); - USE_GEN_PATH; + int len; if (home - && (dir_info = get_dir_info (GEN_PATH (home, ".m17n.d")))) - mplist_push (mdatabase__dir_list, Mt, dir_info); + && (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); - update_database_list (); - mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol)) mdatabase_find); mdatabase__loader = (void *(*) (void *)) mdatabase_load; + mdatabase__list = mplist (); + mdatabase__update (); return 0; } void mdatabase__fini (void) { - int i; - MPlist *plist; + 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) { - MDatabaseInfo *dir_info = MPLIST_VAL (plist); + 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; - free (dir_info->filename); - free (dir_info); + /* 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; + } + } + } } - M17N_OBJECT_UNREF (mdatabase__dir_list); - for (i = 0; i < mdb_list.used; i++) + plist = mplist (); + MPLIST_DO (p0, mdatabase__dir_list) + mplist_push (plist, MPLIST_KEY (p0), MPLIST_VAL (p0)); + + while (! MPLIST_TAIL_P (plist)) { - MDatabase *mdb = mdb_list.mdbs + i; + MDatabaseInfo *dir_info = mplist_pop (plist); + MPlist *pl, *p; + int i; + FILE *fp; - if (mdb->loader == load_database) + if (dir_info->status == MDB_STATUS_DISABLED) + continue; + if (! GEN_PATH (path, dir_info->filename, dir_info->len, + MDB_DIR, MDB_DIR_LEN)) + continue; + if (! (fp = fopen (path, "r"))) + continue; + pl = mplist__from_file (fp, NULL); + fclose (fp); + if (! pl) + continue; + MPLIST_DO (p, pl) { - MDatabaseInfo *db_info = mdb->extra_info; + 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_SYMBOL_P (p1); + i++, p1 = MPLIST_NEXT (p1)) + with_wildcard |= ((tags[i] = MPLIST_SYMBOL (p1)) == Masterisk); + if (i == 0 + || tags[0] == Masterisk + || ! MPLIST_MTEXT_P (p1)) + continue; + for (; i < 4; i++) + 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; - free (db_info->filename); - free (db_info); + 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); } - 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 *filename; + 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)); - filename = get_database_filename ((MDatabaseInfo *) mdb->extra_info); + 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); @@ -828,7 +1039,15 @@ mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys) } -/* Check if the database MDB should be reloaded or not. */ +/* 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) @@ -836,31 +1055,180 @@ mdatabase__check (MDatabase *mdb) MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info; struct stat buf; - if (stat (db_info->filename, &buf) < 0) + if (! get_database_file (db_info, &buf)) return -1; - return (db_info->time >= buf.st_ctime - && db_info->time >= buf.st_mtime); + 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; } -/* Find a file FILENAME in mdatabase__dir_list. If the file exist, - return the absolute pathname. If FILENAME is already absolute, - return a copy of it. */ +/* 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; - db_info.filename = strdup (filename); + 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; - if (! get_database_filename (&db_info)) + 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.filename); - return NULL; + 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; } - return db_info.filename; + 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 */ @@ -916,8 +1284,11 @@ char *mdatabase_dir; MDatabase * mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) { - update_database_list (); - return find_database (tag0, tag1, tag2, tag3); + MSymbol tags[4]; + + mdatabase__update (); + tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3; + return find_database (tags); } /*=*/ @@ -940,25 +1311,43 @@ mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) MPlist * mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) { - int i; - MPlist *plist = NULL, *pl; + MPlist *plist = mplist (), *pl = plist; + MPlist *p, *p0, *p1, *p2, *p3; - update_database_list (); + 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; } @@ -1013,33 +1402,13 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3, void *extra_info) { MDatabase *mdb; + MSymbol tags[4]; - mdb = mdatabase_find (tag0, tag1, tag2, tag3); - if (! mdb) - { - MDatabase template; - - template.tag[0] = tag0, template.tag[1] = tag1; - template.tag[2] = tag2, template.tag[3] = tag3; - 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) - { - MDatabaseInfo *db_info = mdb->extra_info; - - if (db_info) - free (db_info->filename); - else - db_info = mdb->extra_info = malloc (sizeof (MDatabaseInfo)); - db_info->filename = strdup ((char *) extra_info); - db_info->time = 0; - } - 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; } /*=*/ @@ -1099,11 +1468,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); }