1 /* database.c -- database module.
2 Copyright (C) 2003, 2004
3 National Institute of Advanced Industrial Science and Technology (AIST)
4 Registration Number H15PRO112
6 This file is part of the m17n library.
8 The m17n library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public License
10 as published by the Free Software Foundation; either version 2.1 of
11 the License, or (at your option) any later version.
13 The m17n library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public
19 License along with the m17n library; if not, write to the Free
20 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 @addtogroup m17nDatabase
25 @brief The m17n database and API for it.
27 The m17n library acquires various kinds of information
28 from data in the <i> m17n database</i> on demand. Application
29 programs can also add/load their original data to/from the m17n
30 database by setting the variable #mdatabase_dir to an
31 application-specific directory and storing data in it. Users can
32 overwrite those data by storing preferable data in the directory
33 specified by the environment variable "M17NDIR", or if it is not
34 set, in the directory "~/.m17n.d".
36 The m17n database contains multiple heterogeneous data, and each
37 data is identified by four tags; TAG0, TAG1, TAG2, TAG3. Each tag
40 TAG0 specifies the type of data stored in the database as below.
43 If TAG0 is #Mchar_table, the data is of the @e chartable @e
44 type and provides information about each character. In this case,
45 TAG1 specifies the type of the information and must be #Msymbol,
46 #Minteger, #Mstring, #Mtext, or #Mplist. TAG2 and TAG3 can be any
50 If TAG0 is #Mcharset, the data is of the @e charset @e type
51 and provides a decode/encode mapping table for a charset. In this
52 case, TAG1 must be a symbol representing a charset. TAG2 and TAG3
56 If TAG0 is neither #Mchar_table nor #Mcharset, the data is of
57 the @e plist @e type. See the documentation of the
58 mdatabase_load () function for the details.
59 In this case, TAG1, TAG2, and TAG3 can be any symbols.
61 The notation \<TAG0, TAG1, TAG2, TAG3\> means a data with those
64 Application programs first calls the mdatabase_find () function to
65 get a pointer to an object of the type #MDatabase. That object
66 holds information about the specified data. When it is
67 successfully returned, the mdatabase_load () function loads the
68 data. The implementation of the structure #MDatabase is
69 concealed from application programs.
73 @addtogroup m17nDatabase
74 @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤È¤½¤ì¤Ë´Ø¤¹¤ë API.
76 m17n ¥é¥¤¥Ö¥é¥ê¤ÏɬÍפ˱þ¤¸¤ÆưŪ¤Ë @e m17n @e ¥Ç¡¼¥¿¥Ù¡¼¥¹
77 ¤«¤é¾ðÊó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤â¡¢Æȼ«¤Î¥Ç¡¼¥¿¤ò
78 m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤¤ë¡£
79 ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬Æȼ«¤Î¥Ç¡¼¥¿¤òÄɲᦼèÆÀ¤¹¤ë¤Ë¤Ï¡¢ÊÑ¿ô
80 #mdatabase_dir ¤Ë¤½¤Î¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇͤΥǥ£¥ì¥¯¥È¥ê¤ò¥»¥Ã¥È¤·¡¢
81 ¤½¤ÎÃæ¤Ë¥Ç¡¼¥¿¤ò³ÊǼ¤¹¤ë¡£¥æ¡¼¥¶¤¬¤½¤Î¥Ç¡¼¥¿¤ò¥ª¡¼¥Ð¡¼¥é¥¤¥È¤·¤¿¤¤
82 ¤È¤¤Ï¡¢´Ä¶ÊÑ¿ô "M17NDIR" ¤Ç»ØÄꤵ¤ì¤ë¥Ç¥£¥ì¥¯¥È¥ê¡Ê»ØÄꤵ¤ì¤Æ¤¤¤Ê
83 ¤¤¤È¤¤Ï "~/.m17n.d" ¤È¤¤¤¦¥Ç¥£¥ì¥¯¥È¥ê¡Ë¤ËÊ̤Υǡ¼¥¿¤òÃÖ¤¯¡£
86 ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤ÏÊ£¿ô¤Î¿Íͤʥǡ¼¥¿¤¬´Þ¤Þ¤ì¤Æ¤ª¤ê¡¢³Æ¥Ç¡¼¥¿¤Ï
87 TAG0, TAG1, TAG2, TAG3¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Î£´¤Ä¤Î¥¿¥°¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£
89 TAG0 ¤Ë¤è¤Ã¤Æ¡¢¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ï¼¡¤Î¤è¤¦¤Ë»ØÄꤵ¤ì¤ë¡£
92 TAG0 ¤¬ #Mchar_table ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e chartable¥¿¥¤¥×
93 ¤È¸Æ¤Ð¤ì¡¢³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç
94 TAG1 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Msymbol, #Minteger, #Mstring,
95 #Mtext, #Mplist ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
98 TAG0 ¤¬ #Mcharset ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e charset¥¿¥¤¥×
99 ¤È¸Æ¤Ð¤ì¡¢Ê¸»ú¥»¥Ã¥ÈÍѤΥǥ³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥Þ¥Ã¥×¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1
100 ¤Ïʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£TAG2 ¤È TAG3
101 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
104 TAG0 ¤¬ #Mchar_table ¤Ç¤â #Mcharset ¤Ç¤â¤Ê¤¤¾ì¹ç¡¢¤½¤Î¥Ç¡¼¥¿¤Ï @e
105 plist¥¿¥¤¥× ¤Ç¤¢¤ë¡£¾ÜºÙ¤Ë´Ø¤·¤Æ¤Ï´Ø¿ô mdatabase_load ()
106 ¤ÎÀâÌÀ¤ò»²¾È¤Î¤³¤È¡£¤³¤Î¾ì¹ç TAG1¡¢TAG2¡¢TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
108 ÆÃÄê¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò \<TAG0, TAG1, TAG2, TAG3\>
111 ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢¤Þ¤º´Ø¿ô mdatabase_find ()
112 ¤ò»È¤Ã¤Æ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÝ»ý¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¡Ê#MDatabase
113 ·¿¡Ë¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÆÀ¤ë¡£¤½¤ì¤ËÀ®¸ù¤·¤¿¤é¡¢ mdatabase_load ()
114 ¤Ë¤è¤Ã¤Æ¼ÂºÝ¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤¹¤ë¡£¹½Â¤ÂÎ #MDatabase
115 ¼«¿È¤¬¤É¤¦¼ÂÁõ¤µ¤ì¤Æ¤¤¤ë¤«¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£
117 @latexonly \IPAlabel{database} @endlatexonly
122 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
123 /*** @addtogroup m17nInternal
131 #include <sys/types.h>
132 #include <sys/stat.h>
140 #include "m17n-misc.h"
141 #include "internal.h"
143 #include "character.h"
145 #include "database.h"
149 /** The file containing a list of databases. */
150 #define MDB_DIR "mdb.dir"
151 /** Length of MDB_DIR. */
152 #define MDB_DIR_LEN 7
154 #define MAX_TIME(TIME1, TIME2) ((TIME1) >= (TIME2) ? (TIME1) : (TIME2))
156 #define GEN_PATH(path, dir, dir_len, file, file_len) \
157 (dir_len + file_len > PATH_MAX ? 0 \
158 : (memcpy (path, dir, dir_len), \
159 memcpy (path + dir_len, file, file_len), \
160 path[dir_len + file_len] = '\0', 1))
162 static MSymbol Masterisk;
164 /** Structure for a data in the m17n database. */
168 /** Tags to identify the data. <tag>[0] specifies the type of
169 database. If it is #Mchar_table, the type is @e chartable, if
170 it is #Mcharset, the type is @e charset, otherwise the type is
174 void *(*loader) (MSymbol *tags, void *extra_info);
176 /** The meaning of the value is dependent on <loader>. If <loader>
177 is load_database (), the value is a string of the file name that
178 contains the data. */
182 static MPlist *mdatabase__list;
185 read_number (char *buf, int *i)
194 while (c && isspace (c)) c = buf[idx++];
200 for (idx++, c = 0; (n = hex_mnemonic[(unsigned) buf[idx]]) < 16;
214 n = escape_mnemonic[c];
218 while (buf[idx] && buf[idx++] != '\'');
222 else if (hex_mnemonic[c] < 10)
227 while ((n = hex_mnemonic[(unsigned) buf[idx]]) < 10)
228 c = (c * 10) + n, idx++;
234 /** Load a data of type @c chartable from the file FD, and return the
235 newly created chartable. */
238 load_chartable (FILE *fp, MSymbol type)
246 MERROR (MERROR_DB, NULL);
248 table = mchartable (type, (type == Msymbol ? (void *) Mnil
249 : type == Minteger ? (void *) -1
256 for (len = 0; len < 1023 && (c = getc (fp)) != EOF && c != '\n'; len++)
259 if (hex_mnemonic[(unsigned) buf[0]] >= 10)
260 /* skip comment/invalid line */
263 from = read_number (buf, &i);
265 i++, to = read_number (buf, &i);
268 if (from < 0 || to < from)
271 while (buf[i] && isspace ((unsigned) buf[i])) i++;
278 /* VAL is a C-string. */
279 if (! (val = strdup (buf + i)))
280 MEMORY_FULL (MERROR_DB);
282 else if (type == Minteger)
284 /* VAL is an integer. */
290 n = read_number (buf, &i);
293 val = (void *) (n * positive);
295 else if (type == Mtext)
297 /* VAL is an M-text. */
300 mt = mconv_decode_buffer (Mcoding_utf_8,
301 (unsigned char *) (buf + i),
306 while ((c = read_number (buf, &i)) >= 0)
307 mt = mtext_cat_char (mt, c);
311 else if (type == Msymbol)
315 while (*p && ! isspace (*p))
317 if (*p == '\\' && p[1] != '\0')
319 memmove (p, p + 1, buf + len - (p + 1));
325 if (! strcmp (buf + i, "nil"))
328 val = (void *) msymbol (buf + i);
330 else if (type == Mplist)
332 val = (void *) mplist__from_string ((unsigned char *) buf + i,
339 mchartable_set (table, from, val);
341 mchartable_set_range (table, from, to, val);
346 M17N_OBJECT_UNREF (table);
347 MERROR (MERROR_DB, NULL);
351 /** Load a data of type @c charset from the file FD. */
354 load_charset (FILE *fp, MSymbol charset_name)
356 MCharset *charset = MCHARSET (charset_name);
365 MERROR (MERROR_DB, NULL);
366 size = (charset->code_range[15]
367 - (charset->min_code - charset->code_range_min_code));
368 MTABLE_MALLOC (decoder, size, MERROR_DB);
369 for (i = 0; i < size; i++)
371 encoder = mchartable (Minteger, (void *) MCHAR_INVALID_CODE);
373 while ((c = getc (fp)) != EOF)
375 unsigned code1, code2, c1, c2;
380 fgets (buf, 256, fp);
383 if (sscanf (buf, "0x%x-0x%x 0x%x", &code1, &code2, &c1) == 3)
385 idx1 = CODE_POINT_TO_INDEX (charset, code1);
388 idx2 = CODE_POINT_TO_INDEX (charset, code2);
391 c2 = c1 + (idx2 - idx1);
393 else if (sscanf (buf, "0x%x 0x%x", &code1, &c1) == 2)
395 idx1 = idx2 = CODE_POINT_TO_INDEX (charset, code1);
402 if (idx1 >= 0 && idx2 >= 0)
405 mchartable_set (encoder, c1, (void *) code1);
406 for (idx1++, c1++; idx1 <= idx2; idx1++, c1++)
408 code1 = INDEX_TO_CODE_POINT (charset, idx1);
410 mchartable_set (encoder, c1, (void *) code1);
420 M17N_OBJECT_UNREF (encoder);
424 mplist_add (plist, Mt, decoder);
425 mplist_add (plist, Mt, encoder);
430 gen_database_name (char *buf, MSymbol *tags)
434 strcpy (buf, msymbol_name (tags[0]));
435 for (i = 1; i < 4; i++)
438 strcat (buf, msymbol_name (tags[i]));
444 find_file (MDatabaseInfo *db_info, struct stat *buf)
447 char path[PATH_MAX + 1];
449 MPLIST_DO (plist, mdatabase__dir_list)
451 MDatabaseInfo *dir_info = MPLIST_VAL (plist);
453 if (dir_info->status != MDB_STATUS_DISABLED
454 && GEN_PATH (path, dir_info->filename, dir_info->len,
455 db_info->filename, db_info->len)
456 && stat (path, buf) == 0)
457 return strdup (path);
463 /* Return the absolute file name for DB_INFO->filename. If BUF is
464 non-NULL, store the result of `stat' call in it. It returns NULL
465 if no absolute file name was found. */
468 get_database_file (MDatabaseInfo *db_info, struct stat *buf)
470 if (db_info->status == MDB_STATUS_DISABLED)
472 if (db_info->absolute_filename)
475 stat (db_info->absolute_filename, buf);
479 struct stat stat_buf;
480 struct stat *statbuf = buf ? buf : &stat_buf;
482 db_info->absolute_filename = find_file (db_info, statbuf);
485 return db_info->absolute_filename;
489 load_database (MSymbol *tags, void *extra_info)
491 MDatabaseInfo *db_info = extra_info;
493 char *filename = get_database_file (db_info, NULL);
496 if (! filename || ! (fp = fopen (filename, "r")))
497 MERROR (MERROR_DB, NULL);
499 if (tags[0] == Mchar_table)
500 value = load_chartable (fp, tags[1]);
501 else if (tags[0] == Mcharset)
502 value = load_charset (fp, tags[1]);
504 value = mplist__from_file (fp, NULL);
508 MERROR (MERROR_DB, NULL);
509 db_info->time = time (NULL);
514 /** Return a newly allocated MDatabaseInfo for DIRNAME. */
516 static MDatabaseInfo *
517 get_dir_info (char *dirname)
519 MDatabaseInfo *dir_info;
521 MSTRUCT_CALLOC (dir_info, MERROR_DB);
524 int len = strlen (dirname);
525 MTABLE_MALLOC (dir_info->filename, len + 2, MERROR_DB);
526 memcpy (dir_info->filename, dirname, len + 1);
527 /* Append PATH_SEPARATOR if DIRNAME doesn't end with it. */
528 if (dir_info->filename[len - 1] != PATH_SEPARATOR)
530 dir_info->filename[len] = PATH_SEPARATOR;
531 dir_info->filename[++len] = '\0';
534 dir_info->status = MDB_STATUS_AUTO;
537 dir_info->status = MDB_STATUS_DISABLED;
542 find_database (MSymbol tags[4])
547 if (! mdatabase__list)
549 for (i = 0, plist = mdatabase__list; i < 4; i++)
551 plist = mplist__assq (plist, tags[i]);
554 plist = MPLIST_PLIST (plist);
555 plist = MPLIST_NEXT (plist);
557 return MPLIST_VAL (plist);
561 free_db_info (MDatabaseInfo *db_info)
563 free (db_info->filename);
564 if (db_info->absolute_filename
565 && db_info->filename != db_info->absolute_filename)
566 free (db_info->absolute_filename);
571 register_database (MSymbol tags[4], void *(*loader) (MSymbol *, void *),
572 void *extra_info, enum MDatabaseStatus status)
574 MDatabase *mdb = find_database (tags);
575 MDatabaseInfo *db_info = NULL;
579 if (loader == load_database)
580 db_info = mdb->extra_info;
587 MSTRUCT_MALLOC (mdb, MERROR_DB);
588 for (i = 0; i < 4; i++)
589 mdb->tag[i] = tags[i];
590 mdb->loader = loader;
591 if (loader == load_database)
593 MSTRUCT_CALLOC (db_info, MERROR_DB);
594 mdb->extra_info = db_info;
597 mdb->extra_info = extra_info;
598 if (! mdatabase__list)
599 mdatabase__list = mplist ();
600 for (i = 0, plist = mdatabase__list; i < 4; i++)
602 MPlist *pl = mplist__assq (plist, tags[i]);
605 pl = MPLIST_PLIST (pl);
609 mplist_add (pl, Msymbol, tags[i]);
610 mplist_push (plist, Mplist, pl);
611 M17N_OBJECT_UNREF (pl);
613 plist = MPLIST_NEXT (pl);
615 mplist_push (plist, Mt, mdb);
620 db_info->status = status;
621 if (! db_info->filename
622 || strcmp (db_info->filename, (char *) extra_info) != 0)
624 if (db_info->filename)
625 free (db_info->filename);
626 if (db_info->absolute_filename
627 && db_info->filename != db_info->absolute_filename)
628 free (db_info->absolute_filename);
629 db_info->filename = strdup ((char *) extra_info);
630 db_info->len = strlen ((char *) extra_info);
633 if (db_info->filename[0] == PATH_SEPARATOR)
634 db_info->absolute_filename = db_info->filename;
636 db_info->absolute_filename = NULL;
639 if (mdb->tag[0] == Mchar_table
640 && mdb->tag[2] != Mnil
641 && (mdb->tag[1] == Mstring || mdb->tag[1] == Mtext
642 || mdb->tag[1] == Msymbol || mdb->tag[1] == Minteger
643 || mdb->tag[1] == Mplist))
644 mchar__define_prop (mdb->tag[2], mdb->tag[1], mdb);
649 register_databases_in_files (MSymbol tags[4], glob_t *globbuf)
652 MPlist *load_key = mplist ();
656 for (i = 0; i < globbuf->gl_pathc; i++)
658 if (! (fp = fopen (globbuf->gl_pathv[i], "r")))
660 plist = mplist__from_file (fp, load_key);
664 if (MPLIST_PLIST_P (plist))
669 for (j = 0, pl = MPLIST_PLIST (plist); j < 4 && MPLIST_SYMBOL_P (pl);
670 j++, pl = MPLIST_NEXT (pl))
671 tags2[j] = MPLIST_SYMBOL (pl);
674 for (j = 0; j < 4; j++)
675 if (tags[j] == Masterisk ? tags2[j] == Mnil
676 : (tags[j] != Mnil && tags[j] != tags2[j]))
679 register_database (tags2, load_database, globbuf->gl_pathv[i], 1);
681 M17N_OBJECT_UNREF (plist);
683 M17N_OBJECT_UNREF (load_key);
689 /** List of database directories. */
690 MPlist *mdatabase__dir_list;
695 MDatabaseInfo *dir_info;
698 Mchar_table = msymbol ("char-table");
699 Masterisk = msymbol ("*");
701 mdatabase__dir_list = mplist ();
702 /** The macro M17NDIR specifies a directory where the system-wide
703 MDB_DIR file exists. */
704 mplist_set (mdatabase__dir_list, Mt, get_dir_info (M17NDIR));
706 /* The variable mdatabase_dir specifies a directory where an
707 application program specific MDB_DIR file exists. */
708 if (mdatabase_dir && strlen (mdatabase_dir) > 0)
709 mplist_push (mdatabase__dir_list, Mt, get_dir_info (mdatabase_dir));
711 /* The environment variable M17NDIR specifies a directory where a
712 user specific MDB_DIR file exists. */
713 path = getenv ("M17NDIR");
714 if (path && strlen (path) > 0)
715 mplist_push (mdatabase__dir_list, Mt, get_dir_info (path));
718 /* If the env var M17NDIR is not set, check "~/.m17n.d". */
719 char *home = getenv ("HOME");
723 && (len = strlen (home))
724 && (path = alloca (len + 9)))
727 if (path[len - 1] != PATH_SEPARATOR)
728 path[len++] = PATH_SEPARATOR;
729 strcpy (path + len, ".m17n.d");
730 dir_info = get_dir_info (path);
731 mplist_push (mdatabase__dir_list, Mt, dir_info);
734 mplist_push (mdatabase__dir_list, Mt, get_dir_info (NULL));
737 mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
739 mdatabase__loader = (void *(*) (void *)) mdatabase_load;
741 mdatabase__list = mplist ();
742 mdatabase__update ();
747 mdatabase__fini (void)
749 MPlist *plist, *p0, *p1, *p2, *p3;
751 MPLIST_DO (plist, mdatabase__dir_list)
752 free_db_info (MPLIST_VAL (plist));
753 M17N_OBJECT_UNREF (mdatabase__dir_list);
755 /* MDATABASE_LIST ::= ((TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) ...) */
756 MPLIST_DO (plist, mdatabase__list)
758 p0 = MPLIST_PLIST (plist);
759 /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) */
760 MPLIST_DO (p0, MPLIST_NEXT (p0))
762 p1 = MPLIST_PLIST (p0);
763 /* P1 ::= (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) */
764 MPLIST_DO (p1, MPLIST_NEXT (p1))
766 p2 = MPLIST_PLIST (p1);
767 /* P2 ::= (TAG2 (TAG3 t:MDB) ...) */
768 MPLIST_DO (p2, MPLIST_NEXT (p2))
772 p3 = MPLIST_PLIST (p2); /* P3 ::= (TAG3 t:MDB) */
773 p3 = MPLIST_NEXT (p3);
774 mdb = MPLIST_VAL (p3);
775 if (mdb->loader == load_database)
776 free_db_info (mdb->extra_info);
782 M17N_OBJECT_UNREF (mdatabase__list);
786 mdatabase__update (void)
788 MPlist *plist, *p0, *p1, *p2, *p3;
789 char path[PATH_MAX + 1];
790 MDatabaseInfo *dir_info;
793 /* At first, mark all databases defined automatically from mdb.dir
794 file(s) as "disabled". */
795 MPLIST_DO (plist, mdatabase__list)
797 p0 = MPLIST_PLIST (plist);
798 /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
799 MPLIST_DO (p0, MPLIST_NEXT (p0))
801 p1 = MPLIST_PLIST (p0);
802 MPLIST_DO (p1, MPLIST_NEXT (p1))
804 p2 = MPLIST_PLIST (p1);
805 MPLIST_DO (p2, MPLIST_NEXT (p2))
807 MDatabaseInfo *db_info;
809 p3 = MPLIST_PLIST (p2);
810 p3 = MPLIST_NEXT (p3);
811 db_info = MPLIST_VAL (p3);
812 if (db_info->status == MDB_STATUS_AUTO)
813 db_info->status = MDB_STATUS_DISABLED;
819 /* Update elements of mdatabase__dir_list. */
820 MPLIST_DO (plist, mdatabase__dir_list)
822 dir_info = MPLIST_VAL (plist);
823 dir_info->status = ((dir_info->filename
824 && stat (dir_info->filename, &statbuf) == 0
825 && (statbuf.st_mode & S_IFDIR))
826 ? MDB_STATUS_AUTO : MDB_STATUS_DISABLED);
829 MPLIST_DO (plist, mdatabase__dir_list)
831 MDatabaseInfo *dir_info = MPLIST_VAL (plist);
836 if (dir_info->status == MDB_STATUS_DISABLED)
838 if (! GEN_PATH (path, dir_info->filename, dir_info->len,
839 MDB_DIR, MDB_DIR_LEN))
841 if (stat (path, &statbuf) < 0)
843 if (dir_info->time >= statbuf.st_mtime)
845 dir_info->time = statbuf.st_mtime;
846 if (! (fp = fopen (path, "r")))
848 pl = mplist__from_file (fp, NULL);
858 int with_wildcard = 0;
860 if (! MPLIST_PLIST_P (p))
862 for (i = 0, p1 = MPLIST_PLIST (p); i < 4 && MPLIST_SYMBOL_P (p1);
863 i++, p1 = MPLIST_NEXT (p1))
864 with_wildcard |= ((tags[i] = MPLIST_SYMBOL (p1)) == Masterisk);
866 || tags[0] == Masterisk
867 || ! MPLIST_MTEXT_P (p1))
871 mt = MPLIST_MTEXT (p1);
872 if (mt->format >= MTEXT_FORMAT_UTF_16LE)
873 mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
874 nbytes = mtext_nbytes (mt);
875 if (nbytes > PATH_MAX)
877 memcpy (path, MTEXT_DATA (mt), nbytes);
884 if (tags[0] == Mchar_table || tags[0] == Mcharset)
886 if (path[0] == PATH_SEPARATOR)
888 if (glob (path, GLOB_NOSORT, NULL, &globbuf))
890 register_databases_in_files (tags, &globbuf);
894 MPLIST_DO (dlist, mdatabase__dir_list)
896 MDatabaseInfo *d_info = MPLIST_VAL (dlist);
898 if (d_info->status == MDB_STATUS_DISABLED)
900 if (! GEN_PATH (path, d_info->filename, d_info->len,
901 MTEXT_DATA (mt), nbytes))
903 if (glob (path, GLOB_NOSORT, NULL, &globbuf))
905 register_databases_in_files (tags, &globbuf);
911 register_database (tags, load_database, path, 1);
914 M17N_OBJECT_UNREF (pl);
919 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
921 int mdebug_mask = MDEBUG_DATABASE;
922 MDatabaseInfo *db_info;
928 if (mdb->loader != load_database
929 || mdb->tag[0] == Mchar_table
930 || mdb->tag[0] == Mcharset)
931 MERROR (MERROR_DB, NULL);
932 MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
933 gen_database_name (name, mdb->tag));
934 db_info = mdb->extra_info;
935 filename = get_database_file (db_info, NULL);
936 if (! filename || ! (fp = fopen (filename, "r")))
937 MERROR (MERROR_DB, NULL);
938 plist = mplist__from_file (fp, keys);
944 /* Check if the database MDB should be reloaded or not. It returns:
946 1: The database has not been updated since it was loaded last
949 0: The database has never been loaded or has been updated
950 since it was loaded last time.
952 -1: The database is not loadable at the moment. */
955 mdatabase__check (MDatabase *mdb)
957 MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
960 if (! get_database_file (db_info, &buf))
962 if (db_info->time < buf.st_mtime)
967 /* Search directories in mdatabase__dir_list for file FILENAME. If
968 the file exist, return the absolute pathname. If FILENAME is
969 already absolute, return a copy of it. */
972 mdatabase__find_file (char *filename)
975 MDatabaseInfo db_info;
977 if (filename[0] == PATH_SEPARATOR)
978 return (stat (filename, &buf) == 0 ? filename : NULL);
979 db_info.filename = filename;
980 db_info.len = strlen (filename);
982 db_info.absolute_filename = NULL;
983 if (! get_database_file (&db_info, &buf)
984 || stat (db_info.absolute_filename, &buf) < 0)
986 return db_info.absolute_filename;
990 mdatabase__file (MDatabase *mdb)
992 MDatabaseInfo *db_info;
994 if (mdb->loader != load_database)
996 db_info = mdb->extra_info;
997 return get_database_file (db_info, NULL);
1001 mdatabase__lock (MDatabase *mdb)
1003 MDatabaseInfo *db_info;
1009 if (mdb->loader != load_database)
1011 db_info = mdb->extra_info;
1012 if (db_info->lock_file)
1014 file = get_database_file (db_info, NULL);
1017 len = strlen (file);
1018 db_info->uniq_file = malloc (len + 35);
1019 if (! db_info->uniq_file)
1021 db_info->lock_file = malloc (len + 5);
1022 if (! db_info->lock_file)
1024 free (db_info->uniq_file);
1027 sprintf (db_info->uniq_file, "%s.%X.%X", db_info->absolute_filename,
1028 (unsigned) time (NULL), (unsigned) getpid ());
1029 sprintf (db_info->lock_file, "%s.LCK", db_info->absolute_filename);
1031 fp = fopen (db_info->uniq_file, "w");
1034 char *str = strdup (db_info->uniq_file);
1035 char *dir = dirname (str);
1037 if (stat (dir, &buf) == 0
1038 || mkdir (dir, 0777) < 0
1039 || ! (fp = fopen (db_info->uniq_file, "w")))
1041 free (db_info->uniq_file);
1042 free (db_info->lock_file);
1043 db_info->lock_file = NULL;
1050 if (link (db_info->uniq_file, db_info->lock_file) < 0
1051 && (stat (db_info->uniq_file, &buf) < 0
1052 || buf.st_nlink != 2))
1054 unlink (db_info->uniq_file);
1055 unlink (db_info->lock_file);
1056 free (db_info->uniq_file);
1057 free (db_info->lock_file);
1058 db_info->lock_file = NULL;
1065 mdatabase__save (MDatabase *mdb, MPlist *data)
1067 MDatabaseInfo *db_info;
1073 if (mdb->loader != load_database)
1075 db_info = mdb->extra_info;
1076 if (! db_info->lock_file)
1078 file = get_database_file (db_info, NULL);
1082 if (mplist__serialize (mt, data, 1) < 0)
1084 M17N_OBJECT_UNREF (mt);
1087 fp = fopen (db_info->uniq_file, "w");
1090 M17N_OBJECT_UNREF (mt);
1093 mconv_encode_stream (msymbol ("utf-8"), mt, fp);
1094 M17N_OBJECT_UNREF (mt);
1096 if ((ret = rename (db_info->uniq_file, file)) < 0)
1097 unlink (db_info->uniq_file);
1098 free (db_info->uniq_file);
1099 db_info->uniq_file = NULL;
1104 mdatabase__unlock (MDatabase *mdb)
1106 MDatabaseInfo *db_info;
1108 if (mdb->loader != load_database)
1110 db_info = mdb->extra_info;
1111 if (! db_info->lock_file)
1113 unlink (db_info->lock_file);
1114 free (db_info->lock_file);
1115 db_info->lock_file = NULL;
1116 if (db_info->uniq_file)
1118 unlink (db_info->uniq_file);
1119 free (db_info->uniq_file);
1125 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1130 /*** @addtogroup m17nDatabase */
1135 @brief Directory for application specific data.
1137 If an application program wants to provide a data specific to the
1138 program or a data overriding what supplied by the m17n database,
1139 it must set this variable to a name of directory that contains the
1140 data files before it calls the macro M17N_INIT (). The directory
1141 may contain a file "mdb.dir" which contains a list of data
1142 definitions in the format described in @ref mdbDir "mdbDir(5)".
1144 The default value is NULL. */
1146 @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇͤΥǡ¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
1148 ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇͤΥǡ¼¥¿¤ä m17n
1149 ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT ()
1150 ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
1151 "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢
1152 @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ½Ò¤¹¤ë¡£
1154 ¥Ç¥Õ¥©¥ë¥È¤ÎÃÍ¤Ï NULL ¤Ç¤¢¤ë¡£ */
1156 char *mdatabase_dir;
1160 @brief Look for a data in the database.
1162 The mdatabase_find () function searches the m17n database for a
1163 data who has tags $TAG0 through $TAG3, and returns a pointer to
1164 the data. If such a data is not found, it returns @c NULL. */
1167 @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
1169 ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é
1170 $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
1173 @latexonly \IPAlabel{mdatabase_find} @endlatexonly */
1176 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1180 mdatabase__update ();
1181 tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1182 return find_database (tags);
1187 @brief Return a data list of the m17n database.
1189 The mdatabase_list () function searches the m17n database for data
1190 who have tags $TAG0 through $TAG3, and returns their list by a
1191 plist. The value #Mnil in $TAGn means a wild card that matches
1192 any tag. Each element of the plist has key #Mt and value a
1193 pointer to type #MDatabase. */
1195 @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
1197 ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3
1198 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
1199 ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
1200 plist ¤Î³ÆÍ×ÁǤϥ¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£ */
1203 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1205 MPlist *plist = mplist (), *pl = plist;
1206 MPlist *p, *p0, *p1, *p2, *p3;
1208 mdatabase__update ();
1210 MPLIST_DO (p, mdatabase__list)
1212 p0 = MPLIST_PLIST (p);
1213 /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
1214 if (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0)
1216 MPLIST_DO (p0, MPLIST_NEXT (p0))
1218 p1 = MPLIST_PLIST (p0);
1219 if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1)
1221 MPLIST_DO (p1, MPLIST_NEXT (p1))
1223 p2 = MPLIST_PLIST (p1);
1224 if (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2)
1226 MPLIST_DO (p2, MPLIST_NEXT (p2))
1228 p3 = MPLIST_PLIST (p2);
1229 if (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3)
1231 p3 = MPLIST_NEXT (p3);
1232 pl = mplist_add (pl, Mt, MPLIST_VAL (p3));
1237 if (MPLIST_TAIL_P (plist))
1239 M17N_OBJECT_UNREF (plist);
1247 @brief Define a data of the m17n database.
1249 The mdatabase_define () function defines a data that has tags
1250 $TAG0 through $TAG3 and additional information $EXTRA_INFO.
1252 $LOADER is a pointer to a function that loads the data from the
1253 database. This function is called from the mdatabase_load ()
1254 function with the two arguments $TAGS and $EXTRA_INFO. Here,
1255 $TAGS is the array of $TAG0 through $TAG3.
1257 If $LOADER is @c NULL, the default loader of the m17n library is
1258 used. In this case, $EXTRA_INFO must be a string specifying a
1259 filename that contains the data.
1262 If the operation was successful, mdatabase_define () returns a
1263 pointer to the defined data, which can be used as an argument to
1264 mdatabase_load (). Otherwise, it returns @c NULL. */
1267 @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
1269 ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó
1270 $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
1272 $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
1273 mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç
1274 $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1276 ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
1277 $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
1280 ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define ()
1281 ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load ()
1282 ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
1284 @latexonly \IPAlabel{mdatabase_define} @endlatexonly */
1288 mdatabase_load (), mdatabase_define () */
1291 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
1292 void *(*loader) (MSymbol *, void *),
1298 tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1300 loader = load_database;
1301 mdb = register_database (tags, loader, extra_info, 0);
1307 @brief Load a data from the database.
1309 The mdatabase_load () function loads a data specified in $MDB and
1310 returns the contents. The type of contents depends on the type of
1313 If the data is of the @e plist @e type, this function returns a
1314 pointer to @e plist.
1316 If the database is of the @e chartable @e type, it returns a
1317 chartable. The default value of the chartable is set according to
1318 the second tag of the data as below:
1320 @li If the tag is #Msymbol, the default value is #Mnil.
1321 @li If the tag is #Minteger, the default value is -1.
1322 @li Otherwise, the default value is @c NULL.
1324 If the data is of the @e charset @e type, it returns a plist of length 2
1325 (keys are both #Mt). The value of the first element is an array
1326 of integers that maps code points to the corresponding character
1327 codes. The value of the second element is a chartable of integers
1328 that does the reverse mapping. The charset must be defined in
1333 @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
1335 ´Ø¿ô mdatabase_load () ¤Ï $MDB
1336 ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
1338 ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1340 ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
1341 ʸ»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
1343 @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï #Mnil
1344 @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï -1
1345 @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï @c NULL
1347 ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
1348 ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1349 £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
1350 ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
1352 @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1357 mdatabase_load (), mdatabase_define () */
1360 mdatabase_load (MDatabase *mdb)
1362 int mdebug_mask = MDEBUG_DATABASE;
1365 MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
1366 gen_database_name (buf, mdb->tag));
1367 return (*mdb->loader) (mdb->tag, mdb->extra_info);
1372 @brief Get tags of a data.
1374 The mdatabase_tag () function returns an array of tags (symbols)
1375 that identify the data in $MDB. The length of the array is
1379 @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
1381 ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
1384 @latexonly \IPAlabel{mdatabase_tag} @endlatexonly */
1387 mdatabase_tag (MDatabase *mdb)