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))
808 MDatabaseInfo *db_info;
810 p3 = MPLIST_PLIST (p2);
811 p3 = MPLIST_NEXT (p3);
812 mdb = MPLIST_VAL (p3);
813 db_info = mdb->extra_info;
814 if (db_info->status == MDB_STATUS_AUTO)
815 db_info->status = MDB_STATUS_DISABLED;
821 /* Update elements of mdatabase__dir_list. */
822 MPLIST_DO (plist, mdatabase__dir_list)
824 dir_info = MPLIST_VAL (plist);
825 dir_info->status = ((dir_info->filename
826 && stat (dir_info->filename, &statbuf) == 0
827 && (statbuf.st_mode & S_IFDIR))
828 ? MDB_STATUS_AUTO : MDB_STATUS_DISABLED);
831 MPLIST_DO (plist, mdatabase__dir_list)
833 MDatabaseInfo *dir_info = MPLIST_VAL (plist);
838 if (dir_info->status == MDB_STATUS_DISABLED)
840 if (! GEN_PATH (path, dir_info->filename, dir_info->len,
841 MDB_DIR, MDB_DIR_LEN))
843 if (stat (path, &statbuf) < 0)
845 if (dir_info->time >= statbuf.st_mtime)
847 dir_info->time = statbuf.st_mtime;
848 if (! (fp = fopen (path, "r")))
850 pl = mplist__from_file (fp, NULL);
860 int with_wildcard = 0;
862 if (! MPLIST_PLIST_P (p))
864 for (i = 0, p1 = MPLIST_PLIST (p); i < 4 && MPLIST_SYMBOL_P (p1);
865 i++, p1 = MPLIST_NEXT (p1))
866 with_wildcard |= ((tags[i] = MPLIST_SYMBOL (p1)) == Masterisk);
868 || tags[0] == Masterisk
869 || ! MPLIST_MTEXT_P (p1))
873 mt = MPLIST_MTEXT (p1);
874 if (mt->format >= MTEXT_FORMAT_UTF_16LE)
875 mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
876 nbytes = mtext_nbytes (mt);
877 if (nbytes > PATH_MAX)
879 memcpy (path, MTEXT_DATA (mt), nbytes);
886 if (tags[0] == Mchar_table || tags[0] == Mcharset)
888 if (path[0] == PATH_SEPARATOR)
890 if (glob (path, GLOB_NOSORT, NULL, &globbuf))
892 register_databases_in_files (tags, &globbuf);
896 MPLIST_DO (dlist, mdatabase__dir_list)
898 MDatabaseInfo *d_info = MPLIST_VAL (dlist);
900 if (d_info->status == MDB_STATUS_DISABLED)
902 if (! GEN_PATH (path, d_info->filename, d_info->len,
903 MTEXT_DATA (mt), nbytes))
905 if (glob (path, GLOB_NOSORT, NULL, &globbuf))
907 register_databases_in_files (tags, &globbuf);
913 register_database (tags, load_database, path, 1);
916 M17N_OBJECT_UNREF (pl);
921 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
923 int mdebug_mask = MDEBUG_DATABASE;
924 MDatabaseInfo *db_info;
930 if (mdb->loader != load_database
931 || mdb->tag[0] == Mchar_table
932 || mdb->tag[0] == Mcharset)
933 MERROR (MERROR_DB, NULL);
934 MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
935 gen_database_name (name, mdb->tag));
936 db_info = mdb->extra_info;
937 filename = get_database_file (db_info, NULL);
938 if (! filename || ! (fp = fopen (filename, "r")))
939 MERROR (MERROR_DB, NULL);
940 plist = mplist__from_file (fp, keys);
946 /* Check if the database MDB should be reloaded or not. It returns:
948 1: The database has not been updated since it was loaded last
951 0: The database has never been loaded or has been updated
952 since it was loaded last time.
954 -1: The database is not loadable at the moment. */
957 mdatabase__check (MDatabase *mdb)
959 MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
962 if (! get_database_file (db_info, &buf))
964 if (db_info->time < buf.st_mtime)
969 /* Search directories in mdatabase__dir_list for file FILENAME. If
970 the file exist, return the absolute pathname. If FILENAME is
971 already absolute, return a copy of it. */
974 mdatabase__find_file (char *filename)
977 MDatabaseInfo db_info;
979 if (filename[0] == PATH_SEPARATOR)
980 return (stat (filename, &buf) == 0 ? filename : NULL);
981 db_info.filename = filename;
982 db_info.len = strlen (filename);
984 db_info.absolute_filename = NULL;
985 if (! get_database_file (&db_info, &buf)
986 || stat (db_info.absolute_filename, &buf) < 0)
988 return db_info.absolute_filename;
992 mdatabase__file (MDatabase *mdb)
994 MDatabaseInfo *db_info;
996 if (mdb->loader != load_database)
998 db_info = mdb->extra_info;
999 return get_database_file (db_info, NULL);
1003 mdatabase__lock (MDatabase *mdb)
1005 MDatabaseInfo *db_info;
1011 if (mdb->loader != load_database)
1013 db_info = mdb->extra_info;
1014 if (db_info->lock_file)
1016 file = get_database_file (db_info, NULL);
1019 len = strlen (file);
1020 db_info->uniq_file = malloc (len + 35);
1021 if (! db_info->uniq_file)
1023 db_info->lock_file = malloc (len + 5);
1024 if (! db_info->lock_file)
1026 free (db_info->uniq_file);
1029 sprintf (db_info->uniq_file, "%s.%X.%X", db_info->absolute_filename,
1030 (unsigned) time (NULL), (unsigned) getpid ());
1031 sprintf (db_info->lock_file, "%s.LCK", db_info->absolute_filename);
1033 fp = fopen (db_info->uniq_file, "w");
1036 char *str = strdup (db_info->uniq_file);
1037 char *dir = dirname (str);
1039 if (stat (dir, &buf) == 0
1040 || mkdir (dir, 0777) < 0
1041 || ! (fp = fopen (db_info->uniq_file, "w")))
1043 free (db_info->uniq_file);
1044 free (db_info->lock_file);
1045 db_info->lock_file = NULL;
1052 if (link (db_info->uniq_file, db_info->lock_file) < 0
1053 && (stat (db_info->uniq_file, &buf) < 0
1054 || buf.st_nlink != 2))
1056 unlink (db_info->uniq_file);
1057 unlink (db_info->lock_file);
1058 free (db_info->uniq_file);
1059 free (db_info->lock_file);
1060 db_info->lock_file = NULL;
1067 mdatabase__save (MDatabase *mdb, MPlist *data)
1069 MDatabaseInfo *db_info;
1075 if (mdb->loader != load_database)
1077 db_info = mdb->extra_info;
1078 if (! db_info->lock_file)
1080 file = get_database_file (db_info, NULL);
1084 if (mplist__serialize (mt, data, 1) < 0)
1086 M17N_OBJECT_UNREF (mt);
1089 fp = fopen (db_info->uniq_file, "w");
1092 M17N_OBJECT_UNREF (mt);
1095 mconv_encode_stream (msymbol ("utf-8"), mt, fp);
1096 M17N_OBJECT_UNREF (mt);
1098 if ((ret = rename (db_info->uniq_file, file)) < 0)
1099 unlink (db_info->uniq_file);
1100 free (db_info->uniq_file);
1101 db_info->uniq_file = NULL;
1106 mdatabase__unlock (MDatabase *mdb)
1108 MDatabaseInfo *db_info;
1110 if (mdb->loader != load_database)
1112 db_info = mdb->extra_info;
1113 if (! db_info->lock_file)
1115 unlink (db_info->lock_file);
1116 free (db_info->lock_file);
1117 db_info->lock_file = NULL;
1118 if (db_info->uniq_file)
1120 unlink (db_info->uniq_file);
1121 free (db_info->uniq_file);
1127 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1132 /*** @addtogroup m17nDatabase */
1137 @brief Directory for application specific data.
1139 If an application program wants to provide a data specific to the
1140 program or a data overriding what supplied by the m17n database,
1141 it must set this variable to a name of directory that contains the
1142 data files before it calls the macro M17N_INIT (). The directory
1143 may contain a file "mdb.dir" which contains a list of data
1144 definitions in the format described in @ref mdbDir "mdbDir(5)".
1146 The default value is NULL. */
1148 @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇͤΥǡ¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
1150 ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇͤΥǡ¼¥¿¤ä m17n
1151 ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT ()
1152 ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
1153 "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢
1154 @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ½Ò¤¹¤ë¡£
1156 ¥Ç¥Õ¥©¥ë¥È¤ÎÃÍ¤Ï NULL ¤Ç¤¢¤ë¡£ */
1158 char *mdatabase_dir;
1162 @brief Look for a data in the database.
1164 The mdatabase_find () function searches the m17n database for a
1165 data who has tags $TAG0 through $TAG3, and returns a pointer to
1166 the data. If such a data is not found, it returns @c NULL. */
1169 @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
1171 ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é
1172 $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
1175 @latexonly \IPAlabel{mdatabase_find} @endlatexonly */
1178 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1182 mdatabase__update ();
1183 tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1184 return find_database (tags);
1189 @brief Return a data list of the m17n database.
1191 The mdatabase_list () function searches the m17n database for data
1192 who have tags $TAG0 through $TAG3, and returns their list by a
1193 plist. The value #Mnil in $TAGn means a wild card that matches
1194 any tag. Each element of the plist has key #Mt and value a
1195 pointer to type #MDatabase. */
1197 @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
1199 ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3
1200 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
1201 ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
1202 plist ¤Î³ÆÍ×ÁǤϥ¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£ */
1205 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1207 MPlist *plist = mplist (), *pl = plist;
1208 MPlist *p, *p0, *p1, *p2, *p3;
1210 mdatabase__update ();
1212 MPLIST_DO (p, mdatabase__list)
1214 p0 = MPLIST_PLIST (p);
1215 /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
1216 if (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0)
1218 MPLIST_DO (p0, MPLIST_NEXT (p0))
1220 p1 = MPLIST_PLIST (p0);
1221 if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1)
1223 MPLIST_DO (p1, MPLIST_NEXT (p1))
1225 p2 = MPLIST_PLIST (p1);
1226 if (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2)
1228 MPLIST_DO (p2, MPLIST_NEXT (p2))
1230 p3 = MPLIST_PLIST (p2);
1231 if (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3)
1233 p3 = MPLIST_NEXT (p3);
1234 pl = mplist_add (pl, Mt, MPLIST_VAL (p3));
1239 if (MPLIST_TAIL_P (plist))
1241 M17N_OBJECT_UNREF (plist);
1249 @brief Define a data of the m17n database.
1251 The mdatabase_define () function defines a data that has tags
1252 $TAG0 through $TAG3 and additional information $EXTRA_INFO.
1254 $LOADER is a pointer to a function that loads the data from the
1255 database. This function is called from the mdatabase_load ()
1256 function with the two arguments $TAGS and $EXTRA_INFO. Here,
1257 $TAGS is the array of $TAG0 through $TAG3.
1259 If $LOADER is @c NULL, the default loader of the m17n library is
1260 used. In this case, $EXTRA_INFO must be a string specifying a
1261 filename that contains the data.
1264 If the operation was successful, mdatabase_define () returns a
1265 pointer to the defined data, which can be used as an argument to
1266 mdatabase_load (). Otherwise, it returns @c NULL. */
1269 @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
1271 ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó
1272 $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
1274 $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
1275 mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç
1276 $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1278 ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
1279 $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
1282 ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define ()
1283 ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load ()
1284 ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
1286 @latexonly \IPAlabel{mdatabase_define} @endlatexonly */
1290 mdatabase_load (), mdatabase_define () */
1293 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
1294 void *(*loader) (MSymbol *, void *),
1300 tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1302 loader = load_database;
1303 mdb = register_database (tags, loader, extra_info, 0);
1309 @brief Load a data from the database.
1311 The mdatabase_load () function loads a data specified in $MDB and
1312 returns the contents. The type of contents depends on the type of
1315 If the data is of the @e plist @e type, this function returns a
1316 pointer to @e plist.
1318 If the database is of the @e chartable @e type, it returns a
1319 chartable. The default value of the chartable is set according to
1320 the second tag of the data as below:
1322 @li If the tag is #Msymbol, the default value is #Mnil.
1323 @li If the tag is #Minteger, the default value is -1.
1324 @li Otherwise, the default value is @c NULL.
1326 If the data is of the @e charset @e type, it returns a plist of length 2
1327 (keys are both #Mt). The value of the first element is an array
1328 of integers that maps code points to the corresponding character
1329 codes. The value of the second element is a chartable of integers
1330 that does the reverse mapping. The charset must be defined in
1335 @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
1337 ´Ø¿ô mdatabase_load () ¤Ï $MDB
1338 ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
1340 ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1342 ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
1343 ʸ»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
1345 @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï #Mnil
1346 @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï -1
1347 @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃÍ¤Ï @c NULL
1349 ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
1350 ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1351 £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
1352 ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
1354 @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1359 mdatabase_load (), mdatabase_define () */
1362 mdatabase_load (MDatabase *mdb)
1364 int mdebug_mask = MDEBUG_DATABASE;
1367 MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
1368 gen_database_name (buf, mdb->tag));
1369 return (*mdb->loader) (mdb->tag, mdb->extra_info);
1374 @brief Get tags of a data.
1376 The mdatabase_tag () function returns an array of tags (symbols)
1377 that identify the data in $MDB. The length of the array is
1381 @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
1383 ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
1386 @latexonly \IPAlabel{mdatabase_tag} @endlatexonly */
1389 mdatabase_tag (MDatabase *mdb)