X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=src%2Fdatabase.c;h=ca33ec418e9ec647a0d0577ab3f3a4f208573dd6;hb=4c04d78211538ceef48f25fe8ca5402160e6e2d2;hp=a274458d2fee3425bea95e661747d56c4efb257d;hpb=32f35eec7b9de8b35c61e6d8f273e1b84df05732;p=m17n%2Fm17n-lib.git diff --git a/src/database.c b/src/database.c index a274458..ca33ec4 100644 --- a/src/database.c +++ b/src/database.c @@ -1,5 +1,5 @@ /* database.c -- database module. - Copyright (C) 2003, 2004 + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 National Institute of Advanced Industrial Science and Technology (AIST) Registration Number H15PRO112 @@ -17,19 +17,25 @@ 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. 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. @@ -67,39 +73,46 @@ @addtogroup m17nDatabase @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤È¤½¤ì¤Ë´Ø¤¹¤ë API. - m17n ¥é¥¤¥Ö¥é¥ê¤ÏɬÍפ˱þ¤¸¤ÆưŪ¤Ë @e m17n @e ¥Ç¡¼¥¿¥Ù¡¼¥¹ ¤«¤é¾ð - Êó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤âÆȼ«¤Î¥Ç¡¼¥¿¤ò - m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£m17n - ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤ÏÊ£¿ô¤Î¿Íͤʥǡ¼¥¿¤¬´Þ¤Þ¤ì¤Æ¤ª¤ê¡¢³Æ¥Ç¡¼¥¿¤Ï£´¤Ä¤Î - ¥¿¥° TAG0, TAG1, TAG2, TAG3¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£ + m17n ¥é¥¤¥Ö¥é¥ê¤ÏɬÍפ˱þ¤¸¤ÆưŪ¤Ë @e m17n @e ¥Ç¡¼¥¿¥Ù¡¼¥¹ + ¤«¤é¾ðÊó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤â¡¢Æȼ«¤Î¥Ç¡¼¥¿¤ò + m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£ + ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬Æȼ«¤Î¥Ç¡¼¥¿¤òÄɲᦼèÆÀ¤¹¤ë¤Ë¤Ï¡¢ÊÑ¿ô + #mdatabase_dir ¤Ë¤½¤Î¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¥£¥ì¥¯¥È¥ê¤ò¥»¥Ã¥È¤·¡¢ + ¤½¤ÎÃæ¤Ë¥Ç¡¼¥¿¤ò³ÊǼ¤¹¤ë¡£¥æ¡¼¥¶¤¬¤½¤Î¥Ç¡¼¥¿¤ò¥ª¡¼¥Ð¡¼¥é¥¤¥È¤·¤¿¤¤ + ¤È¤­¤Ï¡¢´Ä¶­ÊÑ¿ô "M17NDIR" ¤Ç»ØÄꤵ¤ì¤ë¥Ç¥£¥ì¥¯¥È¥ê¡Ê»ØÄꤵ¤ì¤Æ¤¤¤Ê + ¤¤¤È¤­¤Ï "~/.m17n.d" ¤È¤¤¤¦¥Ç¥£¥ì¥¯¥È¥ê¡Ë¤ËÊ̤Υǡ¼¥¿¤òÃÖ¤¯¡£ + + m17n + ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤ÏÊ£¿ô¤Î¿Íͤʥǡ¼¥¿¤¬´Þ¤Þ¤ì¤Æ¤ª¤ê¡¢³Æ¥Ç¡¼¥¿¤Ï + TAG0, TAG1, TAG2, TAG3¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Î£´¤Ä¤Î¥¿¥°¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£ - TAG0 ¤Ï¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤ò°Ê²¼¤Î¤è¤¦¤Ë»ØÄꤹ¤ë¡£ + TAG0 ¤Ë¤è¤Ã¤Æ¡¢¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ï¼¡¤Î¤è¤¦¤Ë»ØÄꤵ¤ì¤ë¡£ @li - TAG0 ¤¬ #Mchar_table ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e chartable¥¿¥¤¥× ¤È¸Æ¤Ð¤ì¡¢ - ³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë - ¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Msymbol, #Minteger, #Mstring, #Mtext, #Mplist ¤Î - ¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£ + TAG0 ¤¬ #Mchar_table ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e chartable¥¿¥¤¥× + ¤È¸Æ¤Ð¤ì¡¢³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç + TAG1 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Msymbol, #Minteger, #Mstring, + #Mtext, #Mplist ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£ @li - TAG0 ¤¬ #Mcharset ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e charset¥¿¥¤¥× ¤È¸Æ¤Ð¤ì¡¢Ê¸ - »ú¥»¥Ã¥ÈÍѤΥǥ³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥Þ¥Ã¥×¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1 ¤Ï - ʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó - ¥Ü¥ë¤Ç¤è¤¤¡£ + TAG0 ¤¬ #Mcharset ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e charset¥¿¥¤¥× + ¤È¸Æ¤Ð¤ì¡¢Ê¸»ú¥»¥Ã¥ÈÍѤΥǥ³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥Þ¥Ã¥×¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1 + ¤Ïʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£TAG2 ¤È TAG3 + ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£ @li TAG0 ¤¬ #Mchar_table ¤Ç¤â #Mcharset ¤Ç¤â¤Ê¤¤¾ì¹ç¡¢¤½¤Î¥Ç¡¼¥¿¤Ï @e - plist¥¿¥¤¥× ¤Ç¤¢¤ë¡£¾ÜºÙ¤Ë´Ø¤·¤Æ¤Ï´Ø¿ô mdatabase_load () ¤ÎÀâÌÀ¤ò - »²¾È¤Î¤³¤È¡£¤³¤Î¾ì¹ç TAG1¡¢TAG2¡¢TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£ + plist¥¿¥¤¥× ¤Ç¤¢¤ë¡£¾ÜºÙ¤Ë´Ø¤·¤Æ¤Ï´Ø¿ô mdatabase_load () + ¤ÎÀâÌÀ¤ò»²¾È¤Î¤³¤È¡£¤³¤Î¾ì¹ç TAG1¡¢TAG2¡¢TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£ - ÆÃÄê¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò \ ¤È¤¤¤¦·Á - ¼°¤Çɽ¤ï¤¹¡£ + ÆÃÄê¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò \ + ¤È¤¤¤¦·Á¼°¤Çɽ¤¹¡£ - ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢¤Þ¤º´Ø¿ô mdatabase_find () ¤ò»È¤Ã¤Æ¥Ç¡¼ - ¥¿¥Ù¡¼¥¹¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÝ»ý¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¡Ê#MDatabase ·¿¡Ë¤Ø¤Î - ¥Ý¥¤¥ó¥¿¤òÆÀ¤ë¡£¤½¤ì¤ËÀ®¸ù¤·¤¿¤é¡¢ mdatabase_load () ¤Ë¤è¤Ã¤Æ¼ÂºÝ - ¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤¹¤ë¡£¹½Â¤ÂÎ #MDatabase ¼«¿È¤¬¤É¤¦¼ÂÁõ¤µ¤ì - ¤Æ¤¤¤ë¤«¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£ + ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢¤Þ¤º´Ø¿ô mdatabase_find () + ¤ò»È¤Ã¤Æ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÝ»ý¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¡Ê#MDatabase + ·¿¡Ë¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÆÀ¤ë¡£¤½¤ì¤ËÀ®¸ù¤·¤¿¤é¡¢ mdatabase_load () + ¤Ë¤è¤Ã¤Æ¼ÂºÝ¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤¹¤ë¡£¹½Â¤ÂÎ #MDatabase + ¼«¿È¤¬¤É¤¦¼ÂÁõ¤µ¤ì¤Æ¤¤¤ë¤«¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£ @latexonly \IPAlabel{database} @endlatexonly */ @@ -119,20 +132,33 @@ #include #include #include +#include +#include +#include -#include "m17n.h" +#include "m17n-core.h" #include "m17n-misc.h" #include "internal.h" #include "mtext.h" #include "character.h" -#include "charset.h" #include "database.h" -#include "coding.h" #include "plist.h" /** The file containing a list of databases. */ #define MDB_DIR "mdb.dir" -#define MDB_DIR_LEN 8 +/** Length of MDB_DIR. */ +#define MDB_DIR_LEN 7 + +#define MAX_TIME(TIME1, TIME2) ((TIME1) >= (TIME2) ? (TIME1) : (TIME2)) + +#define GEN_PATH(path, dir, dir_len, file, file_len) \ + (dir_len + file_len > PATH_MAX ? 0 \ + : (memcpy (path, dir, dir_len), \ + memcpy (path + dir_len, file, file_len), \ + path[dir_len + file_len] = '\0', 1)) + +static MSymbol Masterisk; +static MSymbol Mversion; /** Structure for a data in the m17n database. */ @@ -152,15 +178,7 @@ struct MDatabase void *extra_info; }; -/** List of all data. */ -struct MDatabaseList -{ - int size, inc, used; - MDatabase *mdbs; -}; - -static struct MDatabaseList mdb_list; - +static MPlist *mdatabase__list; static int read_number (char *buf, int *i) @@ -246,13 +264,13 @@ load_chartable (FILE *fp, MSymbol type) i++, to = read_number (buf, &i); else to = from; - if (from < 0 || to < 0) - goto label_error; + if (from < 0 || to < from) + continue; while (buf[i] && isspace ((unsigned) buf[i])) i++; c = buf[i]; if (!c) - break; + continue; if (type == Mstring) { @@ -278,9 +296,7 @@ load_chartable (FILE *fp, MSymbol type) /* VAL is an M-text. */ MText *mt; if (c == '"') - mt = mconv_decode_buffer (Mcoding_utf_8, - (unsigned char *) (buf + i), - len - i - 1); + mt = mtext__from_data (buf + i, len - i - 1, MTEXT_FORMAT_UTF_8, 1); else { mt = mtext (); @@ -291,6 +307,18 @@ load_chartable (FILE *fp, MSymbol type) } else if (type == Msymbol) { + char *p = buf + i; + + while (*p && ! isspace (*p)) + { + if (*p == '\\' && p[1] != '\0') + { + memmove (p, p + 1, buf + len - (p + 1)); + len--; + } + p++; + } + *p = '\0'; if (! strcmp (buf + i, "nil")) val = (void *) Mnil; else @@ -317,84 +345,6 @@ load_chartable (FILE *fp, MSymbol type) } -/** Load a data of type @c charset from the file FD. */ - -static void * -load_charset (FILE *fp, MSymbol charset_name) -{ - MCharset *charset = MCHARSET (charset_name); - int *decoder; - MCharTable *encoder; - int size; - int i, c; - int found = 0; - MPlist *plist; - - if (! charset) - MERROR (MERROR_DB, NULL); - size = (charset->code_range[15] - - (charset->min_code - charset->code_range_min_code)); - MTABLE_MALLOC (decoder, size, MERROR_DB); - for (i = 0; i < size; i++) - decoder[i] = -1; - encoder = mchartable (Minteger, (void *) MCHAR_INVALID_CODE); - - while ((c = getc (fp)) != EOF) - { - unsigned code1, code2, c1, c2; - int idx1, idx2; - char buf[256]; - - ungetc (c, fp); - fgets (buf, 256, fp); - if (c != '#') - { - if (sscanf (buf, "0x%x-0x%x 0x%x", &code1, &code2, &c1) == 3) - { - idx1 = CODE_POINT_TO_INDEX (charset, code1); - if (idx1 >= size) - continue; - idx2 = CODE_POINT_TO_INDEX (charset, code2); - if (idx2 >= size) - idx2 = size - 1; - c2 = c1 + (idx2 - idx1); - } - else if (sscanf (buf, "0x%x 0x%x", &code1, &c1) == 2) - { - idx1 = idx2 = CODE_POINT_TO_INDEX (charset, code1); - if (idx1 >= size) - continue; - c2 = c1; - } - else - continue; - if (idx1 >= 0 && idx2 >= 0) - { - decoder[idx1] = c1; - mchartable_set (encoder, c1, (void *) code1); - for (idx1++, c1++; idx1 <= idx2; idx1++, c1++) - { - code1 = INDEX_TO_CODE_POINT (charset, idx1); - decoder[idx1] = c1; - mchartable_set (encoder, c1, (void *) code1); - } - found++; - } - } - } - - if (! found) - { - free (decoder); - M17N_OBJECT_UNREF (encoder); - return NULL; - } - plist = mplist (); - mplist_add (plist, Mt, decoder); - mplist_add (plist, Mt, encoder); - return plist; -} - static char * gen_database_name (char *buf, MSymbol *tags) { @@ -403,82 +353,409 @@ gen_database_name (char *buf, MSymbol *tags) strcpy (buf, msymbol_name (tags[0])); for (i = 1; i < 4; i++) { - strcat (buf, ", "); + strcat (buf, ","); strcat (buf, msymbol_name (tags[i])); } return buf; } -static FILE * -get_database_stream (char *filename) -{ - FILE *fp = NULL; +/* Return the absolute file name for DB_INFO->filename or NULL if no + absolute file name was found. If BUF is non-NULL, store the result + of `stat' call in it. In that case, set *RESULT to the return + value of `stat'. */ - if (filename[0] == '/') - fp = fopen (filename, "r"); +char * +get_database_file (MDatabaseInfo *db_info, struct stat *buf, int *result) +{ + if (db_info->absolute_filename) + { + if (buf) + *result = stat (db_info->absolute_filename, buf); + } else { + struct stat stat_buf; + struct stat *statbuf = buf ? buf : &stat_buf; + int res; MPlist *plist; - char path[PATH_MAX]; + char path[PATH_MAX + 1]; MPLIST_DO (plist, mdatabase__dir_list) { - strcpy (path, (char *) MPLIST_VAL (plist)); - strcat (path, filename); - fp = fopen (path, "r"); - if (fp) - break; + MDatabaseInfo *dir_info = MPLIST_VAL (plist); + + if (dir_info->status != MDB_STATUS_DISABLED + && GEN_PATH (path, dir_info->filename, dir_info->len, + db_info->filename, db_info->len) + && (res = stat (path, statbuf)) == 0) + { + db_info->absolute_filename = strdup (path); + if (result) + *result = res; + break; + } } } - return fp; + + return db_info->absolute_filename; } static void * load_database (MSymbol *tags, void *extra_info) { - FILE *fp = get_database_stream ((char *) extra_info); + MDatabaseInfo *db_info = extra_info; void *value; + char *filename = get_database_file (db_info, NULL, NULL); + FILE *fp; + int mdebug_flag = MDEBUG_DATABASE; + char buf[256]; - if (! fp) - MERROR (MERROR_DB, NULL); + MDEBUG_PRINT1 (" [DB] <%s>", gen_database_name (buf, tags)); + if (! filename || ! (fp = fopen (filename, "r"))) + { + if (filename) + MDEBUG_PRINT1 (" open fail: %s\n", filename); + else + MDEBUG_PRINT1 (" not found: %s\n", db_info->filename); + MERROR (MERROR_DB, NULL); + } + + MDEBUG_PRINT1 (" from %s\n", filename); if (tags[0] == Mchar_table) value = load_chartable (fp, tags[1]); else if (tags[0] == Mcharset) - value = load_charset (fp, tags[1]); + { + if (! mdatabase__load_charset_func) + MERROR (MERROR_DB, NULL); + value = (*mdatabase__load_charset_func) (fp, tags[1]); + } else value = mplist__from_file (fp, NULL); fclose (fp); if (! value) MERROR (MERROR_DB, NULL); + db_info->time = time (NULL); return value; } -/** Copy DIRNAME to a newly allocated memory and return it. If - DIRNAME does not end with a slash, append a slash to the new memory. */ +/** Return a newly allocated MDatabaseInfo for DIRNAME. */ -static char * -duplicate_dirname (char *dirname) +static MDatabaseInfo * +get_dir_info (char *dirname) { - struct stat buf; - int len; - char *str; + MDatabaseInfo *dir_info; + + MSTRUCT_CALLOC (dir_info, MERROR_DB); + if (dirname) + { + int len = strlen (dirname); + + if (len + MDB_DIR_LEN < PATH_MAX) + { + MTABLE_MALLOC (dir_info->filename, len + 2, MERROR_DB); + memcpy (dir_info->filename, dirname, len + 1); + /* Append PATH_SEPARATOR if DIRNAME doesn't end with it. */ + if (dir_info->filename[len - 1] != PATH_SEPARATOR) + { + dir_info->filename[len] = PATH_SEPARATOR; + dir_info->filename[++len] = '\0'; + } + dir_info->len = len; + dir_info->status = MDB_STATUS_OUTDATED; + } + else + dir_info->status = MDB_STATUS_DISABLED; + } + else + dir_info->status = MDB_STATUS_DISABLED; + return dir_info; +} + +static void register_databases_in_files (MSymbol tags[4], + char *filename, int len); - if (! dirname - || stat (dirname, &buf) < 0) +static MDatabase * +find_database (MSymbol tags[4]) +{ + MPlist *plist; + int i; + MDatabase *mdb; + + if (! mdatabase__list) return NULL; + for (i = 0, plist = mdatabase__list; i < 4; i++) + { + MPlist *pl = mplist__assq (plist, tags[i]); + MPlist *p; + + if ((p = mplist__assq (plist, Masterisk))) + { + MDatabaseInfo *db_info; + int j; + + p = MPLIST_PLIST (p); + for (j = i + 1; j < 4; j++) + p = MPLIST_PLIST (MPLIST_NEXT (p)); + mdb = MPLIST_VAL (MPLIST_NEXT (p)); + db_info = mdb->extra_info; + if (db_info->status != MDB_STATUS_DISABLED) + { + register_databases_in_files (mdb->tag, + db_info->filename, db_info->len); + db_info->status = MDB_STATUS_DISABLED; + return find_database (tags); + } + } + if (! pl) + return NULL; + plist = MPLIST_PLIST (pl); + plist = MPLIST_NEXT (plist); + } + mdb = MPLIST_VAL (plist); + return mdb; +} + +static void +free_db_info (MDatabaseInfo *db_info) +{ + free (db_info->filename); + if (db_info->absolute_filename + && db_info->filename != db_info->absolute_filename) + free (db_info->absolute_filename); + M17N_OBJECT_UNREF (db_info->properties); + free (db_info); +} - len = strlen (dirname); - MTABLE_MALLOC (str, len + 2, MERROR_DB); - memcpy (str, dirname, len + 1); - if (str[len - 1] != '/') +static int +check_version (MText *version) +{ + char *verstr = (char *) MTEXT_DATA (version); + char *endp = verstr + mtext_nbytes (version); + int ver[3]; + int i; + + ver[0] = ver[1] = ver[2] = 0; + for (i = 0; verstr < endp; verstr++) + { + if (*verstr == '.') + { + i++; + if (i == 3) + break; + continue; + } + if (! isdigit (*verstr)) + break; + ver[i] = ver[i] * 10 + (*verstr - '0'); + } + return (ver[0] < M17NLIB_MAJOR_VERSION + || (ver[0] == M17NLIB_MAJOR_VERSION + && (ver[1] < M17NLIB_MINOR_VERSION + || (ver[1] == M17NLIB_MINOR_VERSION + && ver[2] <= M17NLIB_PATCH_LEVEL)))); +} + +static MDatabase * +register_database (MSymbol tags[4], + void *(*loader) (MSymbol *, void *), + void *extra_info, enum MDatabaseStatus status, + MPlist *properties) +{ + MDatabase *mdb; + MDatabaseInfo *db_info; + int i; + MPlist *plist; + + if (properties) + { + MPLIST_DO (plist, properties) + if (MPLIST_PLIST_P (plist)) + { + MPlist *p = MPLIST_PLIST (plist); + + if (MPLIST_SYMBOL_P (p) + && MPLIST_SYMBOL (p) == Mversion + && MPLIST_MTEXT_P (MPLIST_NEXT (p))) + { + if (check_version (MPLIST_MTEXT (MPLIST_NEXT (p)))) + break; + return NULL; + } + } + } + + for (i = 0, plist = mdatabase__list; i < 4; i++) + { + MPlist *pl = mplist__assq (plist, tags[i]); + + if (pl) + pl = MPLIST_PLIST (pl); + else + { + pl = mplist (); + mplist_add (pl, Msymbol, tags[i]); + mplist_push (plist, Mplist, pl); + M17N_OBJECT_UNREF (pl); + } + plist = MPLIST_NEXT (pl); + } + + if (MPLIST_TAIL_P (plist)) + { + MSTRUCT_MALLOC (mdb, MERROR_DB); + for (i = 0; i < 4; i++) + mdb->tag[i] = tags[i]; + mdb->loader = loader; + if (loader == load_database) + { + MSTRUCT_CALLOC (db_info, MERROR_DB); + mdb->extra_info = db_info; + } + else + { + db_info = NULL; + mdb->extra_info = extra_info; + } + mplist_push (plist, Mt, mdb); + } + else + { + mdb = MPLIST_VAL (plist); + if (loader == load_database) + db_info = mdb->extra_info; + else + db_info = NULL; + } + + if (db_info) + { + db_info->status = status; + if (! db_info->filename + || strcmp (db_info->filename, (char *) extra_info) != 0) + { + if (db_info->filename) + free (db_info->filename); + if (db_info->absolute_filename + && db_info->filename != db_info->absolute_filename) + free (db_info->absolute_filename); + db_info->filename = strdup ((char *) extra_info); + db_info->len = strlen ((char *) extra_info); + db_info->time = 0; + } + if (db_info->filename[0] == PATH_SEPARATOR) + db_info->absolute_filename = db_info->filename; + else + db_info->absolute_filename = NULL; + M17N_OBJECT_UNREF (db_info->properties); + if (properties) + { + db_info->properties = properties; + M17N_OBJECT_REF (properties); + } + } + + if (mdb->tag[0] == Mchar_table + && mdb->tag[2] != Mnil + && (mdb->tag[1] == Mstring || mdb->tag[1] == Mtext + || mdb->tag[1] == Msymbol || mdb->tag[1] == Minteger + || mdb->tag[1] == Mplist)) + mchar__define_prop (mdb->tag[2], mdb->tag[1], mdb); + return mdb; +} + +static void +register_databases_in_files (MSymbol tags[4], char *filename, int len) +{ + int i, j; + MPlist *load_key = mplist (); + FILE *fp; + MPlist *plist, *pl; + + MPLIST_DO (plist, mdatabase__dir_list) + { + glob_t globbuf; + int headlen; + + if (filename[0] == PATH_SEPARATOR) + { + if (glob (filename, GLOB_NOSORT, NULL, &globbuf)) + break; + headlen = 0; + } + else + { + MDatabaseInfo *d_info = MPLIST_VAL (plist); + char path[PATH_MAX + 1]; + + if (d_info->status == MDB_STATUS_DISABLED) + continue; + if (! GEN_PATH (path, d_info->filename, d_info->len, filename, len)) + continue; + if (glob (path, GLOB_NOSORT, NULL, &globbuf)) + continue; + headlen = d_info->len; + } + + for (i = 0; i < globbuf.gl_pathc; i++) + { + if (! (fp = fopen (globbuf.gl_pathv[i], "r"))) + continue; + pl = mplist__from_file (fp, load_key); + fclose (fp); + if (! pl) + continue; + if (MPLIST_PLIST_P (pl)) + { + MPlist *p; + MSymbol tags2[4]; + + for (j = 0, p = MPLIST_PLIST (pl); j < 4 && MPLIST_SYMBOL_P (p); + j++, p = MPLIST_NEXT (p)) + tags2[j] = MPLIST_SYMBOL (p); + for (; j < 4; j++) + tags2[j] = Mnil; + for (j = 0; j < 4; j++) + if (tags[j] != Masterisk && tags[j] != tags2[j]) + break; + if (j == 4) + register_database (tags2, load_database, + globbuf.gl_pathv[i] + headlen, + MDB_STATUS_AUTO, p); + } + M17N_OBJECT_UNREF (pl); + } + globfree (&globbuf); + if (filename[0] == PATH_SEPARATOR) + break; + } + M17N_OBJECT_UNREF (load_key); +} + +static int +expand_wildcard_database (MPlist *plist) +{ + MDatabase *mdb; + MDatabaseInfo *db_info; + + plist = MPLIST_NEXT (plist); + while (MPLIST_PLIST_P (plist)) + { + plist = MPLIST_PLIST (plist); + plist = MPLIST_NEXT (plist); + } + mdb = MPLIST_VAL (plist); + if (mdb->loader == load_database + && (db_info = mdb->extra_info) + && db_info->status != MDB_STATUS_DISABLED) { - str[len] = '/'; - str[len + 1] = '\0'; + register_databases_in_files (mdb->tag, db_info->filename, db_info->len); + db_info->status = MDB_STATUS_DISABLED; + return 1; } - return str; + return 0; } @@ -487,48 +764,193 @@ duplicate_dirname (char *dirname) /** List of database directories. */ MPlist *mdatabase__dir_list; -MSymbol M_database_hook; +void *(*mdatabase__load_charset_func) (FILE *fp, MSymbol charset_name); int mdatabase__init () { - char *dir; - int i; - MPlist *plist; - FILE *fp; + MDatabaseInfo *dir_info; + char *path; + + mdatabase__load_charset_func = NULL; Mchar_table = msymbol ("char-table"); - M_database_hook = msymbol (" database-hook"); + Mcharset = msymbol ("charset"); + Masterisk = msymbol ("*"); + Mversion = msymbol ("version"); mdatabase__dir_list = mplist (); /** The macro M17NDIR specifies a directory where the system-wide MDB_DIR file exists. */ - if ((dir = duplicate_dirname (M17NDIR))) - mplist_set (mdatabase__dir_list, Mt, dir); + mplist_set (mdatabase__dir_list, Mt, get_dir_info (M17NDIR)); /* The variable mdatabase_dir specifies a directory where an application program specific MDB_DIR file exists. */ - if ((dir = duplicate_dirname (mdatabase_dir))) - mplist_push (mdatabase__dir_list, Mt, dir); + if (mdatabase_dir && strlen (mdatabase_dir) > 0) + mplist_push (mdatabase__dir_list, Mt, get_dir_info (mdatabase_dir)); + + /* The environment variable M17NDIR specifies a directory where a + user specific MDB_DIR file exists. */ + path = getenv ("M17NDIR"); + if (path && strlen (path) > 0) + mplist_push (mdatabase__dir_list, Mt, get_dir_info (path)); + else + { + /* If the env var M17NDIR is not set, check "~/.m17n.d". */ + char *home = getenv ("HOME"); + int len; - /* The environment variable M17NDIR (if non-NULL) specifies a - directory where a user specific MDB_DIR file exists. */ - if ((dir = duplicate_dirname (getenv ("M17NDIR")))) - mplist_push (mdatabase__dir_list, Mt, dir); + if (home + && (len = strlen (home)) + && (path = alloca (len + 9))) + { + strcpy (path, home); + if (path[len - 1] != PATH_SEPARATOR) + path[len++] = PATH_SEPARATOR; + strcpy (path + len, ".m17n.d"); + dir_info = get_dir_info (path); + mplist_push (mdatabase__dir_list, Mt, dir_info); + } + else + mplist_push (mdatabase__dir_list, Mt, get_dir_info (NULL)); + } - MLIST_INIT1 (&mdb_list, mdbs, 256); + mdatabase__list = mplist (); + mdatabase__update (); + return 0; +} + +void +mdatabase__fini (void) +{ + MPlist *plist, *p0, *p1, *p2, *p3; + + MPLIST_DO (plist, mdatabase__dir_list) + free_db_info (MPLIST_VAL (plist)); + M17N_OBJECT_UNREF (mdatabase__dir_list); + + /* MDATABASE_LIST ::= ((TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) ...) */ + MPLIST_DO (plist, mdatabase__list) + { + p0 = MPLIST_PLIST (plist); + /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) */ + MPLIST_DO (p0, MPLIST_NEXT (p0)) + { + p1 = MPLIST_PLIST (p0); + /* P1 ::= (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) */ + MPLIST_DO (p1, MPLIST_NEXT (p1)) + { + p2 = MPLIST_PLIST (p1); + /* P2 ::= (TAG2 (TAG3 t:MDB) ...) */ + MPLIST_DO (p2, MPLIST_NEXT (p2)) + { + MDatabase *mdb; + + p3 = MPLIST_PLIST (p2); /* P3 ::= (TAG3 t:MDB) */ + p3 = MPLIST_NEXT (p3); + mdb = MPLIST_VAL (p3); + if (mdb->loader == load_database) + free_db_info (mdb->extra_info); + free (mdb); + } + } + } + } + M17N_OBJECT_UNREF (mdatabase__list); +} + +void +mdatabase__update (void) +{ + MPlist *plist, *p0, *p1, *p2, *p3; + char path[PATH_MAX + 1]; + MDatabaseInfo *dir_info; + struct stat statbuf; + int rescan = 0; + + /* Update elements of mdatabase__dir_list. */ MPLIST_DO (plist, mdatabase__dir_list) { + dir_info = MPLIST_VAL (plist); + if (dir_info->filename) + { + if (stat (dir_info->filename, &statbuf) == 0 + && (statbuf.st_mode & S_IFDIR)) + { + if (dir_info->time < statbuf.st_mtime) + { + rescan = 1; + dir_info->time = statbuf.st_mtime; + } + if (GEN_PATH (path, dir_info->filename, dir_info->len, + MDB_DIR, MDB_DIR_LEN) + && stat (path, &statbuf) >= 0 + && dir_info->time < statbuf.st_mtime) + { + rescan = 1; + dir_info->time = statbuf.st_mtime; + } + dir_info->status = MDB_STATUS_UPDATED; + } + else + { + if (dir_info->status != MDB_STATUS_DISABLED) + { + rescan = 1; + dir_info->time = 0; + dir_info->status = MDB_STATUS_DISABLED; + } + } + } + } + + if (! rescan) + return; + + /* At first, mark all databases defined automatically from mdb.dir + file(s) as "disabled". */ + MPLIST_DO (plist, mdatabase__list) + { + p0 = MPLIST_PLIST (plist); + /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */ + MPLIST_DO (p0, MPLIST_NEXT (p0)) + { + p1 = MPLIST_PLIST (p0); + MPLIST_DO (p1, MPLIST_NEXT (p1)) + { + p2 = MPLIST_PLIST (p1); + MPLIST_DO (p2, MPLIST_NEXT (p2)) + { + MDatabase *mdb; + MDatabaseInfo *db_info; + + p3 = MPLIST_PLIST (p2); + p3 = MPLIST_NEXT (p3); + mdb = MPLIST_VAL (p3); + db_info = mdb->extra_info; + if (db_info->status == MDB_STATUS_AUTO) + db_info->status = MDB_STATUS_DISABLED; + } + } + } + } + + plist = mplist (); + MPLIST_DO (p0, mdatabase__dir_list) + mplist_push (plist, MPLIST_KEY (p0), MPLIST_VAL (p0)); + + while (! MPLIST_TAIL_P (plist)) + { + MDatabaseInfo *dir_info = mplist_pop (plist); MPlist *pl, *p; - int len; - char path[PATH_MAX]; + int i; + FILE *fp; - dir = (char *) MPLIST_VAL (plist); - len = strlen (dir); - if (len + MDB_DIR_LEN >= PATH_MAX) + if (dir_info->status == MDB_STATUS_DISABLED) + continue; + if (! GEN_PATH (path, dir_info->filename, dir_info->len, + MDB_DIR, MDB_DIR_LEN)) continue; - memcpy (path, dir, len); - memcpy (path + len, MDB_DIR, MDB_DIR_LEN); if (! (fp = fopen (path, "r"))) continue; pl = mplist__from_file (fp, NULL); @@ -537,86 +959,266 @@ mdatabase__init () continue; MPLIST_DO (p, pl) { - MDatabase mdb; + MSymbol tags[4]; MPlist *p1; + MText *mt; int nbytes; + int with_wildcard = 0; if (! MPLIST_PLIST_P (p)) continue; - for (i = 0, p1 = MPLIST_PLIST (p); - i < 4 && MPLIST_KEY (p1) == Msymbol; + for (i = 0, p1 = MPLIST_PLIST (p); i < 4 && MPLIST_SYMBOL_P (p1); i++, p1 = MPLIST_NEXT (p1)) - mdb.tag[i] = MPLIST_SYMBOL (p1); + with_wildcard |= ((tags[i] = MPLIST_SYMBOL (p1)) == Masterisk); if (i == 0 + || tags[0] == Masterisk || ! MPLIST_MTEXT_P (p1)) continue; for (; i < 4; i++) - mdb.tag[i] = Mnil; - if (mdatabase_find (mdb.tag[0], mdb.tag[1], - mdb.tag[2], mdb.tag[3])) + tags[i] = with_wildcard ? Masterisk : Mnil; + mt = MPLIST_MTEXT (p1); + nbytes = mtext_nbytes (mt); + if (nbytes > PATH_MAX) continue; - - mdb.loader = load_database; - nbytes = mconv_encode_buffer (Mcoding_utf_8, MPLIST_MTEXT (p1), - (unsigned char *) path, PATH_MAX); - if (nbytes < 0 || nbytes >= PATH_MAX) - continue; - path[nbytes++] = '\0'; - mdb.extra_info = (void *) strdup (path); - MLIST_APPEND1 (&mdb_list, mdbs, mdb, MERROR_DB); + memcpy (path, MTEXT_DATA (mt), nbytes); + path[nbytes] = '\0'; + if (with_wildcard) + register_database (tags, load_database, path, + MDB_STATUS_AUTO_WILDCARD, NULL); + else + register_database (tags, load_database, path, + MDB_STATUS_AUTO, p1); } M17N_OBJECT_UNREF (pl); } + M17N_OBJECT_UNREF (plist); +} - mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol)) - mdatabase_find); - mdatabase__loader = (void *(*) (void *)) mdatabase_load; +MPlist * +mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys) +{ + int mdebug_flag = MDEBUG_DATABASE; + MDatabaseInfo *db_info; + char *filename; + FILE *fp; + MPlist *plist; + char name[256]; - return 0; + if (mdb->loader != load_database + || mdb->tag[0] == Mchar_table + || mdb->tag[0] == Mcharset) + MERROR (MERROR_DB, NULL); + MDEBUG_PRINT1 (" [DB] <%s>.\n", + gen_database_name (name, mdb->tag)); + db_info = mdb->extra_info; + filename = get_database_file (db_info, NULL, NULL); + if (! filename || ! (fp = fopen (filename, "r"))) + MERROR (MERROR_DB, NULL); + plist = mplist__from_file (fp, keys); + fclose (fp); + return plist; } -void -mdatabase__fini (void) + +/* Check if the database MDB should be reloaded or not. It returns: + + 1: The database has not been updated since it was loaded last + time. + + 0: The database has never been loaded or has been updated + since it was loaded last time. + + -1: The database is not loadable at the moment. */ + +int +mdatabase__check (MDatabase *mdb) { - int i; - MPlist *plist; + MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info; + struct stat buf; + int result; - MPLIST_DO (plist, mdatabase__dir_list) - free (MPLIST_VAL (plist)); - M17N_OBJECT_UNREF (mdatabase__dir_list); + if (db_info->absolute_filename != db_info->filename + || db_info->status == MDB_STATUS_AUTO) + mdatabase__update (); + + if (! get_database_file (db_info, &buf, &result) + || result < 0) + return -1; + if (db_info->time < buf.st_mtime) + return 0; + return 1; +} - for (i = 0; i < mdb_list.used; i++) +/* Search directories in mdatabase__dir_list for file FILENAME. If + the file exist, return the absolute pathname. If FILENAME is + already absolute, return a copy of it. */ + +char * +mdatabase__find_file (char *filename) +{ + struct stat buf; + int result; + MDatabaseInfo db_info; + + if (filename[0] == PATH_SEPARATOR) + return (stat (filename, &buf) == 0 ? strdup (filename) : NULL); + db_info.filename = filename; + db_info.len = strlen (filename); + db_info.time = 0; + db_info.absolute_filename = NULL; + if (! get_database_file (&db_info, &buf, &result) + || result < 0) + return NULL; + return db_info.absolute_filename; +} + +char * +mdatabase__file (MDatabase *mdb) +{ + MDatabaseInfo *db_info; + + if (mdb->loader != load_database) + return NULL; + db_info = mdb->extra_info; + return get_database_file (db_info, NULL, NULL); +} + +int +mdatabase__lock (MDatabase *mdb) +{ + MDatabaseInfo *db_info; + struct stat buf; + FILE *fp; + int len; + char *file; + + if (mdb->loader != load_database) + return -1; + db_info = mdb->extra_info; + if (db_info->lock_file) + return -1; + file = get_database_file (db_info, NULL, NULL); + if (! file) + return -1; + len = strlen (file); + db_info->uniq_file = malloc (len + 35); + if (! db_info->uniq_file) + return -1; + db_info->lock_file = malloc (len + 5); + if (! db_info->lock_file) { - MDatabase *mdb = mdb_list.mdbs + i; + free (db_info->uniq_file); + return -1; + } + sprintf (db_info->uniq_file, "%s.%X.%X", db_info->absolute_filename, + (unsigned) time (NULL), (unsigned) getpid ()); + sprintf (db_info->lock_file, "%s.LCK", db_info->absolute_filename); - if (mdb->loader == load_database) - free (mdb->extra_info); + fp = fopen (db_info->uniq_file, "w"); + if (! fp) + { + char *str = strdup (db_info->uniq_file); + char *dir = dirname (str); + + if (stat (dir, &buf) == 0 + || mkdir (dir, 0777) < 0 + || ! (fp = fopen (db_info->uniq_file, "w"))) + { + free (db_info->uniq_file); + free (db_info->lock_file); + db_info->lock_file = NULL; + free (str); + return -1; + } + free (str); + } + fclose (fp); + if (link (db_info->uniq_file, db_info->lock_file) < 0 + && (stat (db_info->uniq_file, &buf) < 0 + || buf.st_nlink != 2)) + { + unlink (db_info->uniq_file); + unlink (db_info->lock_file); + free (db_info->uniq_file); + free (db_info->lock_file); + db_info->lock_file = NULL; + return 0; } - MLIST_FREE1 (&mdb_list, mdbs); + return 1; } -MPlist * -mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys) +int +mdatabase__save (MDatabase *mdb, MPlist *data) { - int mdebug_mask = MDEBUG_DATABASE; + MDatabaseInfo *db_info; FILE *fp; - MPlist *plist; - char buf[256]; + char *file; + MText *mt; + int ret; - if (mdb->loader != load_database - || mdb->tag[0] == Mchar_table - || mdb->tag[0] == Mcharset) - MERROR (MERROR_DB, NULL); - MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n", - gen_database_name (buf, mdb->tag)); - fp = get_database_stream ((char *) mdb->extra_info); + if (mdb->loader != load_database) + return -1; + db_info = mdb->extra_info; + if (! db_info->lock_file) + return -1; + file = get_database_file (db_info, NULL, NULL); + if (! file) + return -1; + mt = mtext (); + if (mplist__serialize (mt, data, 1) < 0) + { + M17N_OBJECT_UNREF (mt); + return -1; + } + fp = fopen (db_info->uniq_file, "w"); if (! fp) - MERROR (MERROR_DB, NULL); - plist = mplist__from_file (fp, keys); + { + M17N_OBJECT_UNREF (mt); + return -1; + } + if (mt->format > MTEXT_FORMAT_UTF_8) + mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8); + fwrite (MTEXT_DATA (mt), 1, mtext_nchars (mt), fp); fclose (fp); - return plist; + M17N_OBJECT_UNREF (mt); + if ((ret = rename (db_info->uniq_file, file)) < 0) + unlink (db_info->uniq_file); + free (db_info->uniq_file); + db_info->uniq_file = NULL; + return ret; } +int +mdatabase__unlock (MDatabase *mdb) +{ + MDatabaseInfo *db_info; + + if (mdb->loader != load_database) + return -1; + db_info = mdb->extra_info; + if (! db_info->lock_file) + return -1; + unlink (db_info->lock_file); + free (db_info->lock_file); + db_info->lock_file = NULL; + if (db_info->uniq_file) + { + unlink (db_info->uniq_file); + free (db_info->uniq_file); + } + return 0; +} + +MPlist * +mdatabase__props (MDatabase *mdb) +{ + MDatabaseInfo *db_info; + + if (mdb->loader != load_database) + return NULL; + db_info = mdb->extra_info; + return db_info->properties; +} /*** @} */ #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */ @@ -624,6 +1226,28 @@ mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys) /* 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 + "charset". */ + +/***ja + @brief ¥·¥ó¥Ü¥ë @c Mcharset. + + ¥Ç¥³¡¼¥É¤µ¤ì¤¿ M-text ¤Ï¡¢¥­¡¼¤¬ @c Mcharset + ¤Ç¤¢¤ë¤è¤¦¤Ê¥Æ¥­¥¹¥È¥×¥í¥Ñ¥Æ¥£¤ò»ý¤Ä¡£ + ¥·¥ó¥Ü¥ë @c Mcharset ¤Ï "charset" ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä¡£ */ + +MSymbol Mcharset; +/*=*/ +/*** @} */ +/*=*/ + /*** @addtogroup m17nDatabase */ /*** @{ */ @@ -642,12 +1266,11 @@ mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys) /***ja @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê. - ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n ¥Ç¡¼ - ¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () - ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê - ¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç - ¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ - ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£ + ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n + ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () + ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï + "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ + @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£ ¥Ç¥Õ¥©¥ë¥È¤ÎÃÍ¤Ï NULL ¤Ç¤¢¤ë¡£ */ @@ -665,32 +1288,19 @@ char *mdatabase_dir; @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹. ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é - $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è - ¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£ + $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð + @c NULL ¤òÊÖ¤¹¡£ @latexonly \IPAlabel{mdatabase_find} @endlatexonly */ MDatabase * mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) { - int i; - MDatabaseHookFunc func - = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook); - - if (func) - func (tag0, tag1, tag2, tag3); - - for (i = 0; i < mdb_list.used; i++) - { - MDatabase *mdb = mdb_list.mdbs + i; + MSymbol tags[4]; - if (tag0 == mdb->tag[0] - && tag1 == mdb->tag[1] - && tag2 == mdb->tag[2] - && tag3 == mdb->tag[3]) - return mdb; - } - return NULL; + mdatabase__update (); + tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3; + return find_database (tags); } /*=*/ @@ -706,42 +1316,78 @@ mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹. ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 - ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn - ¤¬ #Mnil ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È - ¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃÍ - ¤È¤·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£ */ - + ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil + ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë + plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£ */ MPlist * mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) { - int i; - MPlist *plist = NULL, *pl; - MDatabaseHookFunc func - = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook); + MPlist *plist = mplist (), *pl = plist; + MPlist *p, *p0, *p1, *p2, *p3; - if (func) - func (tag0, tag1, tag2, tag3); + mdatabase__update (); - for (i = 0; i < mdb_list.used; i++) + MPLIST_DO (p, mdatabase__list) { - MDatabase *mdb = mdb_list.mdbs + i; - - if ((tag0 == Mnil || tag0 == mdb->tag[0]) - && (tag1 == Mnil || tag1 == mdb->tag[1]) - && (tag2 == Mnil || tag2 == mdb->tag[2]) - && (tag3 == Mnil || tag3 == mdb->tag[3])) + p0 = MPLIST_PLIST (p); + /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */ + if (MPLIST_SYMBOL (p0) == Masterisk + || (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0)) + continue; + MPLIST_DO (p0, MPLIST_NEXT (p0)) { - if (! plist) - plist = pl = mplist (); - pl = mplist_add (pl, Mt, mdb); + p1 = MPLIST_PLIST (p0); + if (MPLIST_SYMBOL (p1) == Masterisk) + { + if (expand_wildcard_database (p1)) + { + M17N_OBJECT_UNREF (plist); + return mdatabase_list (tag0, tag1, tag2, tag3); + } + continue; + } + if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1) + continue; + MPLIST_DO (p1, MPLIST_NEXT (p1)) + { + p2 = MPLIST_PLIST (p1); + if (MPLIST_SYMBOL (p2) == Masterisk) + { + if (expand_wildcard_database (p2)) + { + M17N_OBJECT_UNREF (plist); + return mdatabase_list (tag0, tag1, tag2, tag3); + } + continue; + } + if (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2) + continue; + MPLIST_DO (p2, MPLIST_NEXT (p2)) + { + p3 = MPLIST_PLIST (p2); + if (MPLIST_SYMBOL (p3) == Masterisk) + { + if (expand_wildcard_database (p3)) + { + M17N_OBJECT_UNREF (plist); + return mdatabase_list (tag0, tag1, tag2, tag3); + } + continue; + } + if (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3) + continue; + p3 = MPLIST_NEXT (p3); + pl = mplist_add (pl, Mt, MPLIST_VAL (p3)); + } + } } } + if (MPLIST_TAIL_P (plist)) + M17N_OBJECT_UNREF (plist); return plist; } - - /*=*/ /***en @brief Define a data of the m17n database. @@ -766,22 +1412,20 @@ mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) /***ja @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë. - ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղà - ¾ðÊó $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£ + ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó + $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£ - $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³ - ¤Î´Ø¿ô¤Ï mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦2 ¤Ä¤Î - °ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛ - Îó¤Ç¤¢¤ë¡£ + $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï + mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç + $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£ - ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì - ¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê - ¤é¤Ê¤¤¡£ + ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï + $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£ @return - ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼ - ¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () ¤Î°ú¿ô - ¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£ + ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () + ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () + ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£ @latexonly \IPAlabel{mdatabase_define} @endlatexonly */ @@ -795,33 +1439,13 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3, void *extra_info) { MDatabase *mdb; - MDatabaseHookFunc func - = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook); - - if (func) - func (tag0, tag1, tag2, tag3); + MSymbol tags[4]; - mdb = mdatabase_find (tag0, tag1, tag2, tag3); - if (! mdb) - { - MDatabase template; - - template.tag[0] = tag0, template.tag[1] = tag1; - template.tag[2] = tag2, template.tag[3] = tag3; - template.extra_info = NULL; - MLIST_APPEND1 (&mdb_list, mdbs, template, MERROR_DB); - mdb = mdb_list.mdbs + (mdb_list.used - 1); - } - mdb->loader = loader ? loader : load_database; - if (mdb->loader == load_database) - { - if (mdb->extra_info) - free (mdb->extra_info); - mdb->extra_info = strdup ((char *) extra_info); - } - else - mdb->extra_info = extra_info; - return (&(mdb_list.mdbs[mdb_list.used - 1])); + tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3; + if (! loader) + loader = load_database; + mdb = register_database (tags, loader, extra_info, MDB_STATUS_EXPLICIT, NULL); + return mdb; } /*=*/ @@ -854,22 +1478,22 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3, /***ja @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë. - ´Ø¿ô mdatabase_load () ¤Ï $MDB ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤Î - Ãæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£ + ´Ø¿ô mdatabase_load () ¤Ï $MDB + ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£ ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£ - ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£Ê¸»ú¥Æ¡¼¥Ö¥ë - ¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£ + ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£ + ʸ»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£ @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï #Mnil @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï -1 @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï @c NULL - ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë - #Mt ¡Ë¡£ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥× - ¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡££²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö - ¥ë¤Ç¤¢¤ë¡£¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ + ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£ + ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£ + £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£ + ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£ @latexonly \IPAlabel{mdatabase_load} @endlatexonly */ @@ -881,11 +1505,6 @@ mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3, void * mdatabase_load (MDatabase *mdb) { - int mdebug_mask = MDEBUG_DATABASE; - char buf[256]; - - MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n", - gen_database_name (buf, mdb->tag)); return (*mdb->loader) (mdb->tag, mdb->extra_info); } @@ -900,8 +1519,8 @@ mdatabase_load (MDatabase *mdb) /***ja @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë. - ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ - ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï 4 ¤Ç¤¢¤ë¡£ + ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï + 4 ¤Ç¤¢¤ë¡£ @latexonly \IPAlabel{mdatabase_tag} @endlatexonly */