Update comments.
[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, *file;
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, len;
620       FILE *fp;
621       char *file;
622
623       if (stat (dir_info->filename, &statbuf) < 0)
624         continue;
625       if (dir_info->time >= statbuf.st_ctime
626           && dir_info->time >= statbuf.st_mtime)
627         continue;
628       dir_info->time = MAX_TIME (statbuf.st_ctime, statbuf.st_mtime);
629       path = GEN_PATH (dir_info->filename, MDB_DIR);
630       if (! (fp = fopen (path, "r")))
631         continue;
632       pl = mplist__from_file (fp, NULL);
633       fclose (fp);
634       if (! pl)
635         continue;
636       MPLIST_DO (p, pl)
637         {
638           MDatabase mdb;
639           MPlist *p1;
640           MText *mt;
641           int nbytes;
642           int with_wildcard = 0;
643
644           if (! MPLIST_PLIST_P (p))
645             continue;
646           p1 = MPLIST_PLIST (p);
647           if (! MPLIST_SYMBOL_P (p1))
648               continue;
649           for (i = 0, p1 = MPLIST_PLIST (p);
650                i < 4 && MPLIST_KEY (p1) == Msymbol;
651                i++, p1 = MPLIST_NEXT (p1))
652             with_wildcard |= ((mdb.tag[i] = MPLIST_SYMBOL (p1)) == Masterisk);
653           if (i == 0
654               || mdb.tag[0] == Masterisk
655               || ! MPLIST_MTEXT_P (p1))
656             continue;
657           for (; i < 4; i++)
658             mdb.tag[i] = Mnil;
659           mdb.loader = load_database;
660           mt = MPLIST_MTEXT (p1);
661           if (mt->format >= MTEXT_FORMAT_UTF_16LE)
662             mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
663           nbytes = mtext_nbytes (mt);
664 #ifdef PATH_MAX
665           if (nbytes > PATH_MAX)
666             continue;
667 #endif  /* PATH_MAX */
668           if (with_wildcard
669               && mdb.tag[0] != Mchar_table
670               && mdb.tag[0] != Mcharset)
671             {
672               MPlist *dlist;
673               MPlist *load_key = mplist ();
674
675               MPLIST_DO (dlist, mdatabase__dir_list)
676                 {
677                   MDatabaseInfo *d_info = MPLIST_VAL (dlist);
678                   glob_t globbuf;
679
680                   path = GEN_PATH_FROM_MT (d_info->filename, mt);
681                   if (glob (path, GLOB_NOSORT | GLOB_NOCHECK, NULL, &globbuf))
682                     {
683                       if (path[0] == PATH_SEPARATOR)
684                         break;
685                       continue;
686                     }
687                   for (i = 0; i < globbuf.gl_pathc; i++)
688                     {
689                       if (! (fp = fopen (globbuf.gl_pathv[i], "r")))
690                         continue;
691                       p1 = mplist__from_file (fp, load_key);
692                       fclose (fp);
693                       if (! p1)
694                         continue;
695                       if (MPLIST_PLIST_P (p1))
696                         {
697                           MPlist *p0;
698                           MDatabase mdb2;
699
700                           for (j = 0, p0 = MPLIST_PLIST (p1);
701                                j < 4 && MPLIST_SYMBOL_P (p0);
702                                j++, p0 = MPLIST_NEXT (p0))
703                             mdb2.tag[j] = MPLIST_SYMBOL (p0);
704                           for (; j < 4; j++)
705                             mdb2.tag[j] = Mnil;
706                           for (j = 0; j < 4; j++)
707                             if (mdb.tag[j] == Masterisk
708                                 ? mdb2.tag[j] == Mnil
709                                 : (mdb.tag[j] != Mnil
710                                    && mdb.tag[j] != mdb2.tag[j]))
711                               break;
712                           if (j == 4)
713                             register_database (&mdb2, globbuf.gl_pathv[i]);
714                         }
715                       M17N_OBJECT_UNREF (p1);
716                     }
717                   globfree (&globbuf);
718                   if (MTEXT_DATA (mt)[0] == PATH_SEPARATOR)
719                     break;
720                 }
721
722               M17N_OBJECT_UNREF (load_key);
723             }
724           else
725             {
726               path = GEN_PATH_FROM_MT (dir_info->filename, mt);
727               register_database (&mdb, path);
728             }
729         }
730       M17N_OBJECT_UNREF (pl);
731     }
732 }
733
734 \f
735 /* Internal API */
736
737 /** List of database directories.  */ 
738 MPlist *mdatabase__dir_list;
739
740 int
741 mdatabase__init ()
742 {
743   MDatabaseInfo *dir_info;
744
745   Mchar_table = msymbol ("char-table");
746   Masterisk = msymbol ("*");
747
748   mdatabase__dir_list = mplist ();
749   /** The macro M17NDIR specifies a directory where the system-wide
750     MDB_DIR file exists.  */
751   if ((dir_info = get_dir_info (M17NDIR)))
752     mplist_set (mdatabase__dir_list, Mt, dir_info);
753
754   /* The variable mdatabase_dir specifies a directory where an
755      application program specific MDB_DIR file exists.  */
756   if ((dir_info = get_dir_info (mdatabase_dir)))
757     mplist_push (mdatabase__dir_list, Mt, dir_info);
758
759   /* The environment variable M17NDIR (if non-NULL) specifies a
760      directory where a user specific MDB_DIR file exists.  */
761   if ((dir_info = get_dir_info (getenv ("M17NDIR"))))
762     mplist_push (mdatabase__dir_list, Mt, dir_info);
763   /* If M17NDIR is not set, check "~/.m17n.d".  */
764   else
765     {
766       char *home = getenv ("HOME");
767       USE_GEN_PATH;
768
769       if (home
770           && (dir_info = get_dir_info (GEN_PATH (home, ".m17n.d"))))
771         mplist_push (mdatabase__dir_list, Mt, dir_info);
772     }
773
774   MLIST_INIT1 (&mdb_list, mdbs, 256);
775   update_database_list ();
776
777   mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
778                        mdatabase_find);
779   mdatabase__loader = (void *(*) (void *)) mdatabase_load;
780
781   return 0;
782 }
783
784 void
785 mdatabase__fini (void)
786 {
787   int i;
788   MPlist *plist; 
789
790   MPLIST_DO (plist, mdatabase__dir_list)
791     {
792       MDatabaseInfo *dir_info = MPLIST_VAL (plist);
793
794       free (dir_info->filename);
795       free (dir_info);
796     }
797   M17N_OBJECT_UNREF (mdatabase__dir_list);
798
799   for (i = 0; i < mdb_list.used; i++)
800     {
801       MDatabase *mdb = mdb_list.mdbs + i;
802
803       if (mdb->loader == load_database)
804         {
805           MDatabaseInfo *db_info = mdb->extra_info;
806
807           free (db_info->filename);
808           free (db_info);
809         }
810     }
811   MLIST_FREE1 (&mdb_list, mdbs);
812 }
813
814 MPlist *
815 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
816 {
817   int mdebug_mask = MDEBUG_DATABASE;
818   FILE *fp;
819   MPlist *plist;
820   char buf[256];
821   char *filename;
822
823   if (mdb->loader != load_database
824       || mdb->tag[0] == Mchar_table
825       || mdb->tag[0] == Mcharset)
826     MERROR (MERROR_DB, NULL);
827   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
828                  gen_database_name (buf, mdb->tag));
829   filename = get_database_filename ((MDatabaseInfo *) mdb->extra_info);
830   if (! filename || ! (fp = fopen (filename, "r")))
831     MERROR (MERROR_DB, NULL);
832   plist = mplist__from_file (fp, keys);
833   fclose (fp);
834   return plist;
835 }
836
837
838 /* Check if the database MDB should be reloaded or not.  */
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 */