(update_database_list): Delete unused vars.
[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.  */
838
839 int
840 mdatabase__check (MDatabase *mdb)
841 {
842   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
843   struct stat buf;
844
845   if (stat (db_info->filename, &buf) < 0)
846     return -1;
847   return (db_info->time >= buf.st_ctime
848           && db_info->time >= buf.st_mtime);
849 }
850
851 /* Find a file FILENAME in mdatabase__dir_list.  If the file exist,
852    return the absolute pathname.  If FILENAME is already absolute,
853    return a copy of it.  */
854
855 char *
856 mdatabase__find_file (char *filename)
857 {
858   MDatabaseInfo db_info;
859
860   db_info.filename = strdup (filename);
861   db_info.time = 0;
862   if (! get_database_filename (&db_info))
863     {
864       free (db_info.filename);
865       return NULL;
866     }
867   return db_info.filename;
868 }
869
870
871 /*** @} */
872 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
873
874 \f
875 /* External API */
876
877 /*** @addtogroup m17nDatabase */
878 /*** @{ */
879
880 /*=*/
881 /***en
882     @brief Directory for application specific data.
883
884     If an application program wants to provide a data specific to the
885     program or a data overriding what supplied by the m17n database,
886     it must set this variable to a name of directory that contains the
887     data files before it calls the macro M17N_INIT ().  The directory
888     may contain a file "mdb.dir" which contains a list of data
889     definitions in the format described in @ref mdbDir "mdbDir(5)".
890
891     The default value is NULL.  */
892 /***ja
893     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
894
895     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n 
896     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
897     ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
898     "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ 
899     @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
900
901     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
902
903 char *mdatabase_dir;
904
905 /*=*/
906 /***en
907     @brief Look for a data in the database.
908
909     The mdatabase_find () function searches the m17n database for a
910     data who has tags $TAG0 through $TAG3, and returns a pointer to
911     the data.  If such a data is not found, it returns @c NULL.  */
912
913 /***ja
914     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
915
916     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
917     $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
918     @c NULL ¤òÊÖ¤¹¡£
919
920     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
921
922 MDatabase *
923 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
924 {
925   update_database_list ();
926   return find_database (tag0, tag1, tag2, tag3);
927 }
928
929 /*=*/
930 /***en
931     @brief Return a data list of the m17n database.
932
933     The mdatabase_list () function searches the m17n database for data
934     who have tags $TAG0 through $TAG3, and returns their list by a
935     plist.  The value #Mnil in $TAGn means a wild card that matches
936     any tag.  Each element of the plist has key #Mt and value a
937     pointer to type #MDatabase.  */
938 /***ja
939     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
940
941     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
942     ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
943     ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
944     plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
945
946 MPlist *
947 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
948 {
949   int i;
950   MPlist *plist = NULL, *pl;
951
952   update_database_list ();
953
954   for (i = 0; i < mdb_list.used; i++)
955     {
956       MDatabase *mdb = mdb_list.mdbs + i;
957
958       if ((tag0 == Mnil || tag0 == mdb->tag[0])
959           && (tag1 == Mnil || tag1 == mdb->tag[1])
960           && (tag2 == Mnil || tag2 == mdb->tag[2])
961           && (tag3 == Mnil || tag3 == mdb->tag[3]))
962         {
963           if (! plist)
964             plist = pl = mplist ();
965           pl = mplist_add (pl, Mt, mdb);
966         }
967     }
968   return plist;
969 }
970
971 /*=*/
972 /***en
973     @brief Define a data of the m17n database.
974
975     The mdatabase_define () function defines a data that has tags
976     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
977
978     $LOADER is a pointer to a function that loads the data from the
979     database.  This function is called from the mdatabase_load ()
980     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
981     $TAGS is the array of $TAG0 through $TAG3.
982
983     If $LOADER is @c NULL, the default loader of the m17n library is
984     used.  In this case, $EXTRA_INFO must be a string specifying a
985     filename that contains the data.
986
987     @return
988     If the operation was successful, mdatabase_define () returns a
989     pointer to the defined data, which can be used as an argument to
990     mdatabase_load ().  Otherwise, it returns @c NULL.  */
991
992 /***ja
993     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
994
995     ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó 
996     $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
997
998     $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
999     mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç 
1000     $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1001
1002     ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
1003     $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
1004
1005     @return
1006     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
1007     ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
1008     ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
1009
1010     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
1011
1012 /***
1013     @seealso
1014     mdatabase_load (),  mdatabase_define ()  */
1015
1016 MDatabase *
1017 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
1018                   void *(*loader) (MSymbol *, void *),
1019                   void *extra_info)
1020 {
1021   MDatabase *mdb;
1022
1023   mdb = mdatabase_find (tag0, tag1, tag2, tag3);
1024   if (! mdb)
1025     {
1026       MDatabase template;
1027
1028       template.tag[0] = tag0, template.tag[1] = tag1;
1029       template.tag[2] = tag2, template.tag[3] = tag3;
1030       template.extra_info = NULL;
1031       MLIST_APPEND1 (&mdb_list, mdbs, template, MERROR_DB);
1032       mdb = mdb_list.mdbs + (mdb_list.used - 1);
1033     }
1034   mdb->loader = loader ? loader : load_database;
1035   if (mdb->loader == load_database)
1036     {
1037       MDatabaseInfo *db_info = mdb->extra_info;
1038
1039       if (db_info)
1040         free (db_info->filename);
1041       else
1042         db_info = mdb->extra_info = malloc (sizeof (MDatabaseInfo));
1043       db_info->filename = strdup ((char *) extra_info);
1044       db_info->time = 0;
1045     }
1046   else
1047     mdb->extra_info = extra_info;
1048   return (&(mdb_list.mdbs[mdb_list.used - 1]));
1049 }
1050
1051 /*=*/
1052 /***en
1053     @brief Load a data from the database.
1054
1055     The mdatabase_load () function loads a data specified in $MDB and
1056     returns the contents.  The type of contents depends on the type of
1057     the data.
1058
1059     If the data is of the @e plist @e type, this function returns a
1060     pointer to @e plist.
1061
1062     If the database is of the @e chartable @e type, it returns a
1063     chartable.  The default value of the chartable is set according to
1064     the second tag of the data as below:
1065
1066     @li If the tag is #Msymbol, the default value is #Mnil.
1067     @li If the tag is #Minteger, the default value is -1.
1068     @li Otherwise, the default value is @c NULL.
1069
1070     If the data is of the @e charset @e type, it returns a plist of length 2
1071     (keys are both #Mt).  The value of the first element is an array
1072     of integers that maps code points to the corresponding character
1073     codes.  The value of the second element is a chartable of integers
1074     that does the reverse mapping.  The charset must be defined in
1075     advance.  */
1076
1077
1078 /***ja
1079     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
1080
1081     ´Ø¿ô mdatabase_load () ¤Ï $MDB 
1082     ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
1083
1084     ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1085
1086     ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
1087     Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
1088
1089     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
1090     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
1091     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
1092
1093     ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
1094     ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1095     £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
1096     ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
1097
1098     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1099   */
1100
1101 /***
1102     @seealso
1103     mdatabase_load (),  mdatabase_define ()  */
1104
1105 void *
1106 mdatabase_load (MDatabase *mdb)
1107 {
1108   int mdebug_mask = MDEBUG_DATABASE;
1109   char buf[256];
1110
1111   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
1112                  gen_database_name (buf, mdb->tag));
1113   return (*mdb->loader) (mdb->tag, mdb->extra_info);
1114 }
1115
1116 /*=*/
1117 /***en
1118     @brief Get tags of a data.
1119
1120     The mdatabase_tag () function returns an array of tags (symbols)
1121     that identify the data in $MDB.  The length of the array is
1122     four.  */
1123
1124 /***ja
1125     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
1126
1127     ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
1128     4 ¤Ç¤¢¤ë¡£
1129
1130     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
1131
1132 MSymbol *
1133 mdatabase_tag (MDatabase *mdb)
1134 {
1135   return mdb->tag;
1136 }
1137
1138 /*** @} */
1139
1140 /*
1141   Local Variables:
1142   coding: euc-japan
1143   End:
1144 */