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