(mdatabase__check): Add comment.
[m17n/m17n-lib.git] / src / database.c
1 /* database.c -- database module.
2    Copyright (C) 2003, 2004
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
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.
12
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.
17
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
21    02111-1307, USA.  */
22
23 /***en
24     @addtogroup m17nDatabase
25     @brief The m17n database and API for it.
26
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".
35
36     The m17n database contains multiple heterogeneous data, and each
37     data is identified by four tags; TAG0, TAG1, TAG2, TAG3.  Each tag
38     must be a symbol.
39
40     TAG0 specifies the type of data stored in the database as below.
41
42     @li
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
47     symbols.
48
49     @li
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
53     can be any symbols.
54
55     @li 
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.
60
61     The notation \<TAG0, TAG1, TAG2, TAG3\> means a data with those
62     tags.
63
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.
70 */
71
72 /***ja
73     @addtogroup m17nDatabase
74     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤È¤½¤ì¤Ë´Ø¤¹¤ë API.
75
76     m17n ¥é¥¤¥Ö¥é¥ê¤ÏɬÍפ˱þ¤¸¤ÆưŪ¤Ë @e m17n @e ¥Ç¡¼¥¿¥Ù¡¼¥¹ 
77     ¤«¤é¾ðÊó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤â¡¢Æȼ«¤Î¥Ç¡¼¥¿¤ò 
78     m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£
79     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬Æȼ«¤Î¥Ç¡¼¥¿¤òÄɲᦼèÆÀ¤¹¤ë¤Ë¤Ï¡¢ÊÑ¿ô 
80     #mdatabase_dir ¤Ë¤½¤Î¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¥£¥ì¥¯¥È¥ê¤ò¥»¥Ã¥È¤·¡¢
81     ¤½¤ÎÃæ¤Ë¥Ç¡¼¥¿¤ò³ÊǼ¤¹¤ë¡£¥æ¡¼¥¶¤¬¤½¤Î¥Ç¡¼¥¿¤ò¥ª¡¼¥Ð¡¼¥é¥¤¥È¤·¤¿¤¤
82     ¤È¤­¤Ï¡¢´Ä¶­ÊÑ¿ô "M17NDIR" ¤Ç»ØÄꤵ¤ì¤ë¥Ç¥£¥ì¥¯¥È¥ê¡Ê»ØÄꤵ¤ì¤Æ¤¤¤Ê
83     ¤¤¤È¤­¤Ï "~/.m17n.d" ¤È¤¤¤¦¥Ç¥£¥ì¥¯¥È¥ê¡Ë¤ËÊ̤Υǡ¼¥¿¤òÃÖ¤¯¡£
84
85     m17n 
86     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤ÏÊ£¿ô¤Î¿Íͤʥǡ¼¥¿¤¬´Þ¤Þ¤ì¤Æ¤ª¤ê¡¢³Æ¥Ç¡¼¥¿¤Ï
87     TAG0, TAG1, TAG2, TAG3¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Î£´¤Ä¤Î¥¿¥°¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£
88
89     TAG0 ¤Ë¤è¤Ã¤Æ¡¢¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ï¼¡¤Î¤è¤¦¤Ë»ØÄꤵ¤ì¤ë¡£
90
91     @li 
92     TAG0 ¤¬ #Mchar_table ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e chartable¥¿¥¤¥× 
93     ¤È¸Æ¤Ð¤ì¡¢³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç
94     TAG1 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Msymbol, #Minteger, #Mstring,
95     #Mtext, #Mplist ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
96
97     @li 
98     TAG0 ¤¬ #Mcharset ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e charset¥¿¥¤¥× 
99     ¤È¸Æ¤Ð¤ì¡¢Ê¸»ú¥»¥Ã¥ÈÍѤΥǥ³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥Þ¥Ã¥×¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1
100     ¤Ïʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£TAG2 ¤È TAG3
101     ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
102
103     @li
104     TAG0 ¤¬ #Mchar_table ¤Ç¤â #Mcharset ¤Ç¤â¤Ê¤¤¾ì¹ç¡¢¤½¤Î¥Ç¡¼¥¿¤Ï @e
105     plist¥¿¥¤¥× ¤Ç¤¢¤ë¡£¾ÜºÙ¤Ë´Ø¤·¤Æ¤Ï´Ø¿ô mdatabase_load () 
106     ¤ÎÀâÌÀ¤ò»²¾È¤Î¤³¤È¡£¤³¤Î¾ì¹ç TAG1¡¢TAG2¡¢TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
107
108     ÆÃÄê¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò \<TAG0, TAG1, TAG2, TAG3\> 
109     ¤È¤¤¤¦·Á¼°¤Çɽ¤¹¡£
110
111     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢¤Þ¤º´Ø¿ô mdatabase_find () 
112     ¤ò»È¤Ã¤Æ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÝ»ý¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¡Ê#MDatabase
113     ·¿¡Ë¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÆÀ¤ë¡£¤½¤ì¤ËÀ®¸ù¤·¤¿¤é¡¢ mdatabase_load () 
114     ¤Ë¤è¤Ã¤Æ¼ÂºÝ¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤¹¤ë¡£¹½Â¤ÂΠ#MDatabase 
115     ¼«¿È¤¬¤É¤¦¼ÂÁõ¤µ¤ì¤Æ¤¤¤ë¤«¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£
116
117     @latexonly \IPAlabel{database} @endlatexonly
118 */
119
120 /*=*/
121
122 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
123 /*** @addtogroup m17nInternal
124      @{ */
125
126 #include <config.h>
127 #include <stdio.h>
128 #include <stdlib.h>
129 #include <string.h>
130 #include <ctype.h>
131 #include <sys/types.h>
132 #include <sys/stat.h>
133 #include <unistd.h>
134 #include <limits.h>
135 #include <glob.h>
136
137 #include "m17n.h"
138 #include "m17n-misc.h"
139 #include "internal.h"
140 #include "mtext.h"
141 #include "character.h"
142 #include "charset.h"
143 #include "database.h"
144 #include "coding.h"
145 #include "plist.h"
146
147 /** The file containing a list of databases.  */
148 #define MDB_DIR "mdb.dir"
149 #define MDB_DIR_LEN 8
150
151 #define MAX_TIME(TIME1, TIME2) ((TIME1) >= (TIME2) ? (TIME1) : (TIME2))
152
153 #ifndef PATH_MAX
154 #define PATH_MAX 1024
155 #endif
156
157 #define PATH_SEPARATOR '/'
158
159 #define USE_GEN_PATH                                    \
160   char _path_static[PATH_MAX], *_path = _path_static;   \
161   int _path_len = PATH_MAX, _this_len
162
163 #define GEN_PATH(DIR, FILE)                                                  \
164   (_this_len = strlen (DIR) + strlen (FILE) + 2,                             \
165    sprintf (((_this_len > _path_len)                                         \
166              ? (_path_len = _this_len, _path = alloca (_path_len)) : _path), \
167             "%s%c%s", (DIR), PATH_SEPARATOR, (FILE)),                        \
168    _path)
169
170 #define GEN_PATH_FROM_MT(DIR, MT)                                       \
171   (mtext_ref_char ((MT), 0) == PATH_SEPARATOR                           \
172    ? (_this_len = mtext_nbytes (MT) + 1,                                \
173       (_this_len > _path_len                                            \
174        ? (_path_len = _this_len, _path = alloca (_path_len))            \
175        : _path)[_this_len - 1] = '\0',                                  \
176       memcpy (_path, MTEXT_DATA (MT), mtext_nbytes (MT)))               \
177    : (_this_len = strlen (DIR) + mtext_nbytes (MT) + 2,                 \
178       (_this_len > _path_len                                            \
179        ? (_path_len = _this_len, _path = alloca (_path_len))            \
180        : _path)[_this_len - 1] = '\0',                                  \
181       memcpy (_path + sprintf (_path, "%s%c", (DIR), PATH_SEPARATOR),   \
182               MTEXT_DATA (MT), mtext_nbytes (MT)),                      \
183       _path))
184
185 static MSymbol Masterisk;
186
187 /** Structure for a data in the m17n database.  */
188
189 struct MDatabase
190 {
191   /** Tags to identify the data.  <tag>[0] specifies the type of
192       database.  If it is #Mchar_table, the type is @e chartable, if
193       it is #Mcharset, the type is @e charset, otherwise the type is
194       @e plist.  */
195   MSymbol tag[4];
196
197   void *(*loader) (MSymbol *tags, void *extra_info);
198
199   /** The meaning of the value is dependent on <loader>.  If <loader>
200       is load_database (), the value is a string of the file name that
201       contains the data.  */
202   void *extra_info;
203 };
204
205 typedef struct
206 {
207   char *filename;
208   time_t time;
209 } MDatabaseInfo;
210
211 /** List of all data.  */
212 struct MDatabaseList
213 {
214   int size, inc, used;
215   MDatabase *mdbs;
216 };
217
218 static struct MDatabaseList mdb_list;
219
220
221 static int
222 read_number (char *buf, int *i)
223 {
224   int idx = *i;
225   int c = buf[idx++];
226   int n;
227
228   if (!c)
229     return -1;
230
231   while (c && isspace (c)) c = buf[idx++];
232
233   if (c == '0')
234     {
235       if (buf[idx] == 'x')
236         {
237           for (idx++, c = 0; (n = hex_mnemonic[(unsigned) buf[idx]]) < 16;
238                idx++)
239             c  = (c << 4) | n;
240           *i = idx;
241           return c;
242         }
243       c = 0;
244     }
245   else if (c == '\'')
246     {
247       c = buf[idx++];
248       if (c == '\\')
249         {
250           c = buf[idx++];
251           n = escape_mnemonic[c];
252           if (n != 255)
253             c = n;
254         }
255       while (buf[idx] && buf[idx++] != '\'');
256       *i = idx;
257       return c;
258     }
259   else if (hex_mnemonic[c] < 10)
260     c -= '0';
261   else
262     return -1;
263
264   while ((n = hex_mnemonic[(unsigned) buf[idx]]) < 10)
265     c = (c * 10) + n, idx++;
266   *i = idx;
267   return c;
268 }
269
270
271 /** Load a data of type @c chartable from the file FD, and return the
272     newly created chartable.  */
273
274 static void *
275 load_chartable (FILE *fp, MSymbol type)
276 {
277   int c, from, to;
278   char buf[1024];
279   void *val;
280   MCharTable *table;
281
282   if (! fp)
283     MERROR (MERROR_DB, NULL);
284
285   table = mchartable (type, (type == Msymbol ? (void *) Mnil
286                              : type == Minteger ? (void *) -1
287                              : NULL));
288
289   while (! feof (fp))
290     {
291       int i, len;
292
293       for (len = 0; len < 1023 && (c = getc (fp)) != EOF && c != '\n'; len++)
294         buf[len] = c;
295       buf[len] = '\0';    
296       if (hex_mnemonic[(unsigned) buf[0]] >= 10)
297         /* skip comment/invalid line */
298         continue;
299       i = 0;
300       from = read_number (buf, &i);
301       if (buf[i] == '-')
302         i++, to = read_number (buf, &i);
303       else
304         to = from;
305       if (from < 0 || to < from)
306         continue;
307
308       while (buf[i] && isspace ((unsigned) buf[i])) i++;
309       c = buf[i];
310       if (!c)
311         continue;
312
313       if (type == Mstring)
314         {
315           /* VAL is a C-string.  */
316           if (! (val = strdup (buf + i)))
317             MEMORY_FULL (MERROR_DB);
318         }
319       else if (type == Minteger)
320         {
321           /* VAL is an integer.  */
322           int positive = 1;
323           int n;
324
325           if (c == '-')
326             i++, positive = -1;
327           n = read_number (buf, &i);
328           if (n < 0)
329             goto label_error;
330           val = (void *) (n * positive);
331         }
332       else if (type == Mtext)
333         {
334           /* VAL is an M-text.  */
335           MText *mt;
336           if (c == '"')
337             mt = mconv_decode_buffer (Mcoding_utf_8,
338                                       (unsigned char *) (buf + i),
339                                       len - i - 1);
340           else
341             {
342               mt = mtext ();
343               while ((c = read_number (buf, &i)) >= 0)
344                 mt = mtext_cat_char (mt, c);
345             }
346           val = (void *) mt;
347         }
348       else if (type == Msymbol)
349         {
350           char *p = buf + i;
351
352           while (*p && ! isspace (*p)) 
353             {
354               if (*p == '\\' && p[1] != '\0')
355                 {
356                   memmove (p, p + 1, buf + len - (p + 1));
357                   len--;
358                 }
359               p++;
360             }
361           *p = '\0';
362           if (! strcmp (buf + i, "nil"))
363             val = (void *) Mnil;
364           else
365             val = (void *) msymbol (buf + i);
366         }
367       else if (type == Mplist)
368         {
369           val = (void *) mplist__from_string ((unsigned char *) buf + i,
370                                               strlen (buf + i));
371         }
372       else
373         val = NULL;
374
375       if (from == to)
376         mchartable_set (table, from, val);
377       else
378         mchartable_set_range (table, from, to, val);
379     }
380   return table;
381
382  label_error:
383   M17N_OBJECT_UNREF (table);
384   MERROR (MERROR_DB, NULL);
385 }
386
387
388 /** Load a data of type @c charset from the file FD.  */
389
390 static void *
391 load_charset (FILE *fp, MSymbol charset_name)
392 {
393   MCharset *charset = MCHARSET (charset_name);
394   int *decoder;
395   MCharTable *encoder;
396   int size;
397   int i, c;
398   int found = 0;
399   MPlist *plist;
400
401   if (! charset)
402     MERROR (MERROR_DB, NULL);
403   size = (charset->code_range[15]
404           - (charset->min_code - charset->code_range_min_code));
405   MTABLE_MALLOC (decoder, size, MERROR_DB);
406   for (i = 0; i < size; i++)
407     decoder[i] = -1;
408   encoder = mchartable (Minteger, (void *) MCHAR_INVALID_CODE);
409
410   while ((c = getc (fp)) != EOF)
411     {
412       unsigned code1, code2, c1, c2;
413       int idx1, idx2;
414       char buf[256];
415
416       ungetc (c, fp);
417       fgets (buf, 256, fp);
418       if (c != '#')
419         {
420           if (sscanf (buf, "0x%x-0x%x 0x%x", &code1, &code2, &c1) == 3)
421             {
422               idx1 = CODE_POINT_TO_INDEX (charset, code1);
423               if (idx1 >= size)
424                 continue;
425               idx2 = CODE_POINT_TO_INDEX (charset, code2);
426               if (idx2 >= size)
427                 idx2 = size - 1;
428               c2 = c1 + (idx2 - idx1);
429             }
430           else if (sscanf (buf, "0x%x 0x%x", &code1, &c1) == 2)
431             {
432               idx1 = idx2 = CODE_POINT_TO_INDEX (charset, code1);
433               if (idx1 >= size)
434                 continue;
435               c2 = c1;
436             }
437           else
438             continue;
439           if (idx1 >= 0 && idx2 >= 0)
440             {
441               decoder[idx1] = c1;
442               mchartable_set (encoder, c1, (void *) code1);
443               for (idx1++, c1++; idx1 <= idx2; idx1++, c1++)
444                 {
445                   code1 = INDEX_TO_CODE_POINT (charset, idx1);
446                   decoder[idx1] = c1;
447                   mchartable_set (encoder, c1, (void *) code1);
448                 }
449               found++;
450             }
451         }
452     }
453
454   if (! found)
455     {
456       free (decoder);
457       M17N_OBJECT_UNREF (encoder);
458       return NULL;
459     }
460   plist = mplist ();
461   mplist_add (plist, Mt, decoder);
462   mplist_add (plist, Mt, encoder);
463   return plist;
464 }
465
466 static char *
467 gen_database_name (char *buf, MSymbol *tags)
468 {
469   int i;
470
471   strcpy (buf, msymbol_name (tags[0]));
472   for (i = 1; i < 4; i++)
473     {
474       strcat (buf, ", ");
475       strcat (buf, msymbol_name (tags[i]));
476     }
477   return buf;
478 }
479
480 static char *
481 get_database_filename (MDatabaseInfo *db_info)
482 {
483   char *filename = NULL;
484   struct stat buf;
485
486   if (db_info->filename[0] == '/')
487     {
488       if (stat (db_info->filename, &buf) == 0)
489         {
490           filename = db_info->filename;
491           db_info->time = MAX_TIME (buf.st_mtime, buf.st_ctime);
492         }
493     }
494   else
495     {
496       MPlist *plist;
497       USE_GEN_PATH;
498
499       MPLIST_DO (plist, mdatabase__dir_list)
500         {
501           MDatabaseInfo *dir_info = MPLIST_VAL (plist);
502           char *path = GEN_PATH (dir_info->filename, db_info->filename);
503
504           if (stat (path, &buf) == 0)
505             {
506               free (db_info->filename);
507               filename = db_info->filename = strdup (path);
508               db_info->time = MAX_TIME (buf.st_mtime, buf.st_ctime);
509               break;
510             }
511         }
512     }
513   return filename;
514 }
515
516 static void *
517 load_database (MSymbol *tags, void *extra_info)
518 {
519   char *filename = get_database_filename ((MDatabaseInfo *) extra_info);
520   FILE *fp;
521   void *value;
522
523   if (! filename || ! (fp = fopen (filename, "r")))
524     MERROR (MERROR_DB, NULL);
525
526   if (tags[0] == Mchar_table)
527     value = load_chartable (fp, tags[1]);
528   else if (tags[0] == Mcharset)
529     value = load_charset (fp, tags[1]);
530   else
531     value = mplist__from_file (fp, NULL);
532   fclose (fp);
533
534   if (! value)
535     MERROR (MERROR_DB, NULL);
536   return value;
537 }
538
539
540 /** If DIRNAME is a readable directory, allocate MDatabaseInfo and
541     copy DIRNAME to a newly allocated memory and return it.  If
542     DIRNAME does not end with a slash, append a slash to the new memory.  */
543
544 static MDatabaseInfo *
545 get_dir_info (char *dirname)
546 {
547   struct stat buf;
548   int len;
549   MDatabaseInfo *dir_info;
550
551   if (! dirname
552       || stat (dirname, &buf) < 0
553       || ! (buf.st_mode & S_IFDIR))
554     return NULL;
555
556   MSTRUCT_MALLOC (dir_info, MERROR_DB);
557   len = strlen (dirname) + 1;
558   MTABLE_MALLOC (dir_info->filename, len, MERROR_DB);
559   memcpy (dir_info->filename, dirname, len);
560   /* Don't include the last '/' in filename.  */
561   if (dir_info->filename[len - 2] == PATH_SEPARATOR)
562       dir_info->filename[len - 2] = '\0';
563   /* Set this to zero so that the first call of update_database_list
564      surely checks this directory.  */
565   dir_info->time = 0;
566   return dir_info;
567 }
568
569 static MDatabase *
570 find_database (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
571 {
572   int i;
573
574   for (i = 0; i < mdb_list.used; i++)
575     {
576       MDatabase *mdb = mdb_list.mdbs + i;
577
578       if (tag0 == mdb->tag[0]
579           && tag1 == mdb->tag[1]
580           && tag2 == mdb->tag[2]
581           && tag3 == mdb->tag[3])
582         return mdb;
583     }
584   return NULL;
585 }
586
587 static void
588 register_database (MDatabase *mdb, char *path)
589 {
590   if (find_database (mdb->tag[0], mdb->tag[1], mdb->tag[2], mdb->tag[3]))
591     return;
592   mdb->loader = load_database;
593   mdb->extra_info = calloc (1, sizeof (MDatabaseInfo));
594   if (! mdb->extra_info)
595     MEMORY_FULL (MERROR_DB);
596   ((MDatabaseInfo*) mdb->extra_info)->filename = strdup (path);
597   MLIST_APPEND1 (&mdb_list, mdbs, *mdb, MERROR_DB);
598   if (mdb->tag[0] == Mchar_table
599       && mdb->tag[2] != Mnil
600       && (mdb->tag[1] == Mstring || mdb->tag[1] == Mtext
601           || mdb->tag[1] == Msymbol || mdb->tag[1] == Minteger
602           || mdb->tag[1] == Mplist))
603     mchar__define_prop (mdb->tag[2], mdb->tag[1],
604                         mdb_list.mdbs + mdb_list.used - 1);
605 }
606
607 static void
608 update_database_list ()
609 {
610   MPlist *plist;
611   char *path;
612   USE_GEN_PATH;
613
614   MPLIST_DO (plist, mdatabase__dir_list)
615     {
616       MDatabaseInfo *dir_info = MPLIST_VAL (plist);
617       struct stat statbuf;
618       MPlist *pl, *p;
619       int i, j;
620       FILE *fp;
621
622       if (stat (dir_info->filename, &statbuf) < 0)
623         continue;
624       if (dir_info->time >= statbuf.st_ctime
625           && dir_info->time >= statbuf.st_mtime)
626         continue;
627       dir_info->time = MAX_TIME (statbuf.st_ctime, statbuf.st_mtime);
628       path = GEN_PATH (dir_info->filename, MDB_DIR);
629       if (! (fp = fopen (path, "r")))
630         continue;
631       pl = mplist__from_file (fp, NULL);
632       fclose (fp);
633       if (! pl)
634         continue;
635       MPLIST_DO (p, pl)
636         {
637           MDatabase mdb;
638           MPlist *p1;
639           MText *mt;
640           int nbytes;
641           int with_wildcard = 0;
642
643           if (! MPLIST_PLIST_P (p))
644             continue;
645           p1 = MPLIST_PLIST (p);
646           if (! MPLIST_SYMBOL_P (p1))
647               continue;
648           for (i = 0, p1 = MPLIST_PLIST (p);
649                i < 4 && MPLIST_KEY (p1) == Msymbol;
650                i++, p1 = MPLIST_NEXT (p1))
651             with_wildcard |= ((mdb.tag[i] = MPLIST_SYMBOL (p1)) == Masterisk);
652           if (i == 0
653               || mdb.tag[0] == Masterisk
654               || ! MPLIST_MTEXT_P (p1))
655             continue;
656           for (; i < 4; i++)
657             mdb.tag[i] = Mnil;
658           mdb.loader = load_database;
659           mt = MPLIST_MTEXT (p1);
660           if (mt->format >= MTEXT_FORMAT_UTF_16LE)
661             mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
662           nbytes = mtext_nbytes (mt);
663 #ifdef PATH_MAX
664           if (nbytes > PATH_MAX)
665             continue;
666 #endif  /* PATH_MAX */
667           if (with_wildcard
668               && mdb.tag[0] != Mchar_table
669               && mdb.tag[0] != Mcharset)
670             {
671               MPlist *dlist;
672               MPlist *load_key = mplist ();
673
674               MPLIST_DO (dlist, mdatabase__dir_list)
675                 {
676                   MDatabaseInfo *d_info = MPLIST_VAL (dlist);
677                   glob_t globbuf;
678
679                   path = GEN_PATH_FROM_MT (d_info->filename, mt);
680                   if (glob (path, GLOB_NOSORT | GLOB_NOCHECK, NULL, &globbuf))
681                     {
682                       if (path[0] == PATH_SEPARATOR)
683                         break;
684                       continue;
685                     }
686                   for (i = 0; i < globbuf.gl_pathc; i++)
687                     {
688                       if (! (fp = fopen (globbuf.gl_pathv[i], "r")))
689                         continue;
690                       p1 = mplist__from_file (fp, load_key);
691                       fclose (fp);
692                       if (! p1)
693                         continue;
694                       if (MPLIST_PLIST_P (p1))
695                         {
696                           MPlist *p0;
697                           MDatabase mdb2;
698
699                           for (j = 0, p0 = MPLIST_PLIST (p1);
700                                j < 4 && MPLIST_SYMBOL_P (p0);
701                                j++, p0 = MPLIST_NEXT (p0))
702                             mdb2.tag[j] = MPLIST_SYMBOL (p0);
703                           for (; j < 4; j++)
704                             mdb2.tag[j] = Mnil;
705                           for (j = 0; j < 4; j++)
706                             if (mdb.tag[j] == Masterisk
707                                 ? mdb2.tag[j] == Mnil
708                                 : (mdb.tag[j] != Mnil
709                                    && mdb.tag[j] != mdb2.tag[j]))
710                               break;
711                           if (j == 4)
712                             register_database (&mdb2, globbuf.gl_pathv[i]);
713                         }
714                       M17N_OBJECT_UNREF (p1);
715                     }
716                   globfree (&globbuf);
717                   if (MTEXT_DATA (mt)[0] == PATH_SEPARATOR)
718                     break;
719                 }
720
721               M17N_OBJECT_UNREF (load_key);
722             }
723           else
724             {
725               path = GEN_PATH_FROM_MT (dir_info->filename, mt);
726               register_database (&mdb, path);
727             }
728         }
729       M17N_OBJECT_UNREF (pl);
730     }
731 }
732
733 \f
734 /* Internal API */
735
736 /** List of database directories.  */ 
737 MPlist *mdatabase__dir_list;
738
739 int
740 mdatabase__init ()
741 {
742   MDatabaseInfo *dir_info;
743
744   Mchar_table = msymbol ("char-table");
745   Masterisk = msymbol ("*");
746
747   mdatabase__dir_list = mplist ();
748   /** The macro M17NDIR specifies a directory where the system-wide
749     MDB_DIR file exists.  */
750   if ((dir_info = get_dir_info (M17NDIR)))
751     mplist_set (mdatabase__dir_list, Mt, dir_info);
752
753   /* The variable mdatabase_dir specifies a directory where an
754      application program specific MDB_DIR file exists.  */
755   if ((dir_info = get_dir_info (mdatabase_dir)))
756     mplist_push (mdatabase__dir_list, Mt, dir_info);
757
758   /* The environment variable M17NDIR (if non-NULL) specifies a
759      directory where a user specific MDB_DIR file exists.  */
760   if ((dir_info = get_dir_info (getenv ("M17NDIR"))))
761     mplist_push (mdatabase__dir_list, Mt, dir_info);
762   /* If M17NDIR is not set, check "~/.m17n.d".  */
763   else
764     {
765       char *home = getenv ("HOME");
766       USE_GEN_PATH;
767
768       if (home
769           && (dir_info = get_dir_info (GEN_PATH (home, ".m17n.d"))))
770         mplist_push (mdatabase__dir_list, Mt, dir_info);
771     }
772
773   MLIST_INIT1 (&mdb_list, mdbs, 256);
774   update_database_list ();
775
776   mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
777                        mdatabase_find);
778   mdatabase__loader = (void *(*) (void *)) mdatabase_load;
779
780   return 0;
781 }
782
783 void
784 mdatabase__fini (void)
785 {
786   int i;
787   MPlist *plist; 
788
789   MPLIST_DO (plist, mdatabase__dir_list)
790     {
791       MDatabaseInfo *dir_info = MPLIST_VAL (plist);
792
793       free (dir_info->filename);
794       free (dir_info);
795     }
796   M17N_OBJECT_UNREF (mdatabase__dir_list);
797
798   for (i = 0; i < mdb_list.used; i++)
799     {
800       MDatabase *mdb = mdb_list.mdbs + i;
801
802       if (mdb->loader == load_database)
803         {
804           MDatabaseInfo *db_info = mdb->extra_info;
805
806           free (db_info->filename);
807           free (db_info);
808         }
809     }
810   MLIST_FREE1 (&mdb_list, mdbs);
811 }
812
813 MPlist *
814 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
815 {
816   int mdebug_mask = MDEBUG_DATABASE;
817   FILE *fp;
818   MPlist *plist;
819   char buf[256];
820   char *filename;
821
822   if (mdb->loader != load_database
823       || mdb->tag[0] == Mchar_table
824       || mdb->tag[0] == Mcharset)
825     MERROR (MERROR_DB, NULL);
826   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
827                  gen_database_name (buf, mdb->tag));
828   filename = get_database_filename ((MDatabaseInfo *) mdb->extra_info);
829   if (! filename || ! (fp = fopen (filename, "r")))
830     MERROR (MERROR_DB, NULL);
831   plist = mplist__from_file (fp, keys);
832   fclose (fp);
833   return plist;
834 }
835
836
837 /* Check if the database MDB should be reloaded or not.  Return -1 if
838    the database became not reloadable.  */
839
840 int
841 mdatabase__check (MDatabase *mdb)
842 {
843   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
844   struct stat buf;
845
846   if (stat (db_info->filename, &buf) < 0)
847     return -1;
848   return (db_info->time >= buf.st_ctime
849           && db_info->time >= buf.st_mtime);
850 }
851
852 /* Find a file FILENAME in mdatabase__dir_list.  If the file exist,
853    return the absolute pathname.  If FILENAME is already absolute,
854    return a copy of it.  */
855
856 char *
857 mdatabase__find_file (char *filename)
858 {
859   MDatabaseInfo db_info;
860
861   db_info.filename = strdup (filename);
862   db_info.time = 0;
863   if (! get_database_filename (&db_info))
864     {
865       free (db_info.filename);
866       return NULL;
867     }
868   return db_info.filename;
869 }
870
871
872 /*** @} */
873 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
874
875 \f
876 /* External API */
877
878 /*** @addtogroup m17nDatabase */
879 /*** @{ */
880
881 /*=*/
882 /***en
883     @brief Directory for application specific data.
884
885     If an application program wants to provide a data specific to the
886     program or a data overriding what supplied by the m17n database,
887     it must set this variable to a name of directory that contains the
888     data files before it calls the macro M17N_INIT ().  The directory
889     may contain a file "mdb.dir" which contains a list of data
890     definitions in the format described in @ref mdbDir "mdbDir(5)".
891
892     The default value is NULL.  */
893 /***ja
894     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
895
896     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n 
897     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
898     ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
899     "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ 
900     @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
901
902     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
903
904 char *mdatabase_dir;
905
906 /*=*/
907 /***en
908     @brief Look for a data in the database.
909
910     The mdatabase_find () function searches the m17n database for a
911     data who has tags $TAG0 through $TAG3, and returns a pointer to
912     the data.  If such a data is not found, it returns @c NULL.  */
913
914 /***ja
915     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
916
917     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
918     $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
919     @c NULL ¤òÊÖ¤¹¡£
920
921     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
922
923 MDatabase *
924 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
925 {
926   update_database_list ();
927   return find_database (tag0, tag1, tag2, tag3);
928 }
929
930 /*=*/
931 /***en
932     @brief Return a data list of the m17n database.
933
934     The mdatabase_list () function searches the m17n database for data
935     who have tags $TAG0 through $TAG3, and returns their list by a
936     plist.  The value #Mnil in $TAGn means a wild card that matches
937     any tag.  Each element of the plist has key #Mt and value a
938     pointer to type #MDatabase.  */
939 /***ja
940     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
941
942     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
943     ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
944     ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
945     plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
946
947 MPlist *
948 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
949 {
950   int i;
951   MPlist *plist = NULL, *pl;
952
953   update_database_list ();
954
955   for (i = 0; i < mdb_list.used; i++)
956     {
957       MDatabase *mdb = mdb_list.mdbs + i;
958
959       if ((tag0 == Mnil || tag0 == mdb->tag[0])
960           && (tag1 == Mnil || tag1 == mdb->tag[1])
961           && (tag2 == Mnil || tag2 == mdb->tag[2])
962           && (tag3 == Mnil || tag3 == mdb->tag[3]))
963         {
964           if (! plist)
965             plist = pl = mplist ();
966           pl = mplist_add (pl, Mt, mdb);
967         }
968     }
969   return plist;
970 }
971
972 /*=*/
973 /***en
974     @brief Define a data of the m17n database.
975
976     The mdatabase_define () function defines a data that has tags
977     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
978
979     $LOADER is a pointer to a function that loads the data from the
980     database.  This function is called from the mdatabase_load ()
981     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
982     $TAGS is the array of $TAG0 through $TAG3.
983
984     If $LOADER is @c NULL, the default loader of the m17n library is
985     used.  In this case, $EXTRA_INFO must be a string specifying a
986     filename that contains the data.
987
988     @return
989     If the operation was successful, mdatabase_define () returns a
990     pointer to the defined data, which can be used as an argument to
991     mdatabase_load ().  Otherwise, it returns @c NULL.  */
992
993 /***ja
994     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
995
996     ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó 
997     $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
998
999     $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
1000     mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç 
1001     $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1002
1003     ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
1004     $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
1005
1006     @return
1007     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
1008     ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
1009     ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
1010
1011     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
1012
1013 /***
1014     @seealso
1015     mdatabase_load (),  mdatabase_define ()  */
1016
1017 MDatabase *
1018 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
1019                   void *(*loader) (MSymbol *, void *),
1020                   void *extra_info)
1021 {
1022   MDatabase *mdb;
1023
1024   mdb = mdatabase_find (tag0, tag1, tag2, tag3);
1025   if (! mdb)
1026     {
1027       MDatabase template;
1028
1029       template.tag[0] = tag0, template.tag[1] = tag1;
1030       template.tag[2] = tag2, template.tag[3] = tag3;
1031       template.extra_info = NULL;
1032       MLIST_APPEND1 (&mdb_list, mdbs, template, MERROR_DB);
1033       mdb = mdb_list.mdbs + (mdb_list.used - 1);
1034     }
1035   mdb->loader = loader ? loader : load_database;
1036   if (mdb->loader == load_database)
1037     {
1038       MDatabaseInfo *db_info = mdb->extra_info;
1039
1040       if (db_info)
1041         free (db_info->filename);
1042       else
1043         db_info = mdb->extra_info = malloc (sizeof (MDatabaseInfo));
1044       db_info->filename = strdup ((char *) extra_info);
1045       db_info->time = 0;
1046     }
1047   else
1048     mdb->extra_info = extra_info;
1049   return (&(mdb_list.mdbs[mdb_list.used - 1]));
1050 }
1051
1052 /*=*/
1053 /***en
1054     @brief Load a data from the database.
1055
1056     The mdatabase_load () function loads a data specified in $MDB and
1057     returns the contents.  The type of contents depends on the type of
1058     the data.
1059
1060     If the data is of the @e plist @e type, this function returns a
1061     pointer to @e plist.
1062
1063     If the database is of the @e chartable @e type, it returns a
1064     chartable.  The default value of the chartable is set according to
1065     the second tag of the data as below:
1066
1067     @li If the tag is #Msymbol, the default value is #Mnil.
1068     @li If the tag is #Minteger, the default value is -1.
1069     @li Otherwise, the default value is @c NULL.
1070
1071     If the data is of the @e charset @e type, it returns a plist of length 2
1072     (keys are both #Mt).  The value of the first element is an array
1073     of integers that maps code points to the corresponding character
1074     codes.  The value of the second element is a chartable of integers
1075     that does the reverse mapping.  The charset must be defined in
1076     advance.  */
1077
1078
1079 /***ja
1080     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
1081
1082     ´Ø¿ô mdatabase_load () ¤Ï $MDB 
1083     ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
1084
1085     ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1086
1087     ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
1088     Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
1089
1090     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
1091     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
1092     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
1093
1094     ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
1095     ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1096     £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
1097     ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
1098
1099     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1100   */
1101
1102 /***
1103     @seealso
1104     mdatabase_load (),  mdatabase_define ()  */
1105
1106 void *
1107 mdatabase_load (MDatabase *mdb)
1108 {
1109   int mdebug_mask = MDEBUG_DATABASE;
1110   char buf[256];
1111
1112   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
1113                  gen_database_name (buf, mdb->tag));
1114   return (*mdb->loader) (mdb->tag, mdb->extra_info);
1115 }
1116
1117 /*=*/
1118 /***en
1119     @brief Get tags of a data.
1120
1121     The mdatabase_tag () function returns an array of tags (symbols)
1122     that identify the data in $MDB.  The length of the array is
1123     four.  */
1124
1125 /***ja
1126     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
1127
1128     ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
1129     4 ¤Ç¤¢¤ë¡£
1130
1131     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
1132
1133 MSymbol *
1134 mdatabase_tag (MDatabase *mdb)
1135 {
1136   return mdb->tag;
1137 }
1138
1139 /*** @} */
1140
1141 /*
1142   Local Variables:
1143   coding: euc-japan
1144   End:
1145 */