93cc5ff18c4fffe075f20da79d6848316cb084bf
[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 #include <time.h>
137 #include <libgen.h>
138
139 #include "m17n.h"
140 #include "m17n-misc.h"
141 #include "internal.h"
142 #include "mtext.h"
143 #include "character.h"
144 #include "charset.h"
145 #include "database.h"
146 #include "coding.h"
147 #include "plist.h"
148
149 /** The file containing a list of databases.  */
150 #define MDB_DIR "mdb.dir"
151 /** Length of MDB_DIR.  */
152 #define MDB_DIR_LEN 7
153
154 #define MAX_TIME(TIME1, TIME2) ((TIME1) >= (TIME2) ? (TIME1) : (TIME2))
155
156 #define GEN_PATH(path, dir, dir_len, file, file_len)    \
157   (dir_len + file_len > PATH_MAX ? 0                    \
158    : (memcpy (path, dir, dir_len),                      \
159       memcpy (path + dir_len, file, file_len),          \
160       path[dir_len + file_len] = '\0', 1))
161
162 static MSymbol Masterisk;
163
164 /** Structure for a data in the m17n database.  */
165
166 struct MDatabase
167 {
168   /** Tags to identify the data.  <tag>[0] specifies the type of
169       database.  If it is #Mchar_table, the type is @e chartable, if
170       it is #Mcharset, the type is @e charset, otherwise the type is
171       @e plist.  */
172   MSymbol tag[4];
173
174   void *(*loader) (MSymbol *tags, void *extra_info);
175
176   /** The meaning of the value is dependent on <loader>.  If <loader>
177       is load_database (), the value is a string of the file name that
178       contains the data.  */
179   void *extra_info;
180 };
181
182 static MPlist *mdatabase__list;
183
184 static int
185 read_number (char *buf, int *i)
186 {
187   int idx = *i;
188   int c = buf[idx++];
189   int n;
190
191   if (!c)
192     return -1;
193
194   while (c && isspace (c)) c = buf[idx++];
195
196   if (c == '0')
197     {
198       if (buf[idx] == 'x')
199         {
200           for (idx++, c = 0; (n = hex_mnemonic[(unsigned) buf[idx]]) < 16;
201                idx++)
202             c  = (c << 4) | n;
203           *i = idx;
204           return c;
205         }
206       c = 0;
207     }
208   else if (c == '\'')
209     {
210       c = buf[idx++];
211       if (c == '\\')
212         {
213           c = buf[idx++];
214           n = escape_mnemonic[c];
215           if (n != 255)
216             c = n;
217         }
218       while (buf[idx] && buf[idx++] != '\'');
219       *i = idx;
220       return c;
221     }
222   else if (hex_mnemonic[c] < 10)
223     c -= '0';
224   else
225     return -1;
226
227   while ((n = hex_mnemonic[(unsigned) buf[idx]]) < 10)
228     c = (c * 10) + n, idx++;
229   *i = idx;
230   return c;
231 }
232
233
234 /** Load a data of type @c chartable from the file FD, and return the
235     newly created chartable.  */
236
237 static void *
238 load_chartable (FILE *fp, MSymbol type)
239 {
240   int c, from, to;
241   char buf[1024];
242   void *val;
243   MCharTable *table;
244
245   if (! fp)
246     MERROR (MERROR_DB, NULL);
247
248   table = mchartable (type, (type == Msymbol ? (void *) Mnil
249                              : type == Minteger ? (void *) -1
250                              : NULL));
251
252   while (! feof (fp))
253     {
254       int i, len;
255
256       for (len = 0; len < 1023 && (c = getc (fp)) != EOF && c != '\n'; len++)
257         buf[len] = c;
258       buf[len] = '\0';    
259       if (hex_mnemonic[(unsigned) buf[0]] >= 10)
260         /* skip comment/invalid line */
261         continue;
262       i = 0;
263       from = read_number (buf, &i);
264       if (buf[i] == '-')
265         i++, to = read_number (buf, &i);
266       else
267         to = from;
268       if (from < 0 || to < from)
269         continue;
270
271       while (buf[i] && isspace ((unsigned) buf[i])) i++;
272       c = buf[i];
273       if (!c)
274         continue;
275
276       if (type == Mstring)
277         {
278           /* VAL is a C-string.  */
279           if (! (val = strdup (buf + i)))
280             MEMORY_FULL (MERROR_DB);
281         }
282       else if (type == Minteger)
283         {
284           /* VAL is an integer.  */
285           int positive = 1;
286           int n;
287
288           if (c == '-')
289             i++, positive = -1;
290           n = read_number (buf, &i);
291           if (n < 0)
292             goto label_error;
293           val = (void *) (n * positive);
294         }
295       else if (type == Mtext)
296         {
297           /* VAL is an M-text.  */
298           MText *mt;
299           if (c == '"')
300             mt = mconv_decode_buffer (Mcoding_utf_8,
301                                       (unsigned char *) (buf + i),
302                                       len - i - 1);
303           else
304             {
305               mt = mtext ();
306               while ((c = read_number (buf, &i)) >= 0)
307                 mt = mtext_cat_char (mt, c);
308             }
309           val = (void *) mt;
310         }
311       else if (type == Msymbol)
312         {
313           char *p = buf + i;
314
315           while (*p && ! isspace (*p)) 
316             {
317               if (*p == '\\' && p[1] != '\0')
318                 {
319                   memmove (p, p + 1, buf + len - (p + 1));
320                   len--;
321                 }
322               p++;
323             }
324           *p = '\0';
325           if (! strcmp (buf + i, "nil"))
326             val = (void *) Mnil;
327           else
328             val = (void *) msymbol (buf + i);
329         }
330       else if (type == Mplist)
331         {
332           val = (void *) mplist__from_string ((unsigned char *) buf + i,
333                                               strlen (buf + i));
334         }
335       else
336         val = NULL;
337
338       if (from == to)
339         mchartable_set (table, from, val);
340       else
341         mchartable_set_range (table, from, to, val);
342     }
343   return table;
344
345  label_error:
346   M17N_OBJECT_UNREF (table);
347   MERROR (MERROR_DB, NULL);
348 }
349
350
351 /** Load a data of type @c charset from the file FD.  */
352
353 static void *
354 load_charset (FILE *fp, MSymbol charset_name)
355 {
356   MCharset *charset = MCHARSET (charset_name);
357   int *decoder;
358   MCharTable *encoder;
359   int size;
360   int i, c;
361   int found = 0;
362   MPlist *plist;
363
364   if (! charset)
365     MERROR (MERROR_DB, NULL);
366   size = (charset->code_range[15]
367           - (charset->min_code - charset->code_range_min_code));
368   MTABLE_MALLOC (decoder, size, MERROR_DB);
369   for (i = 0; i < size; i++)
370     decoder[i] = -1;
371   encoder = mchartable (Minteger, (void *) MCHAR_INVALID_CODE);
372
373   while ((c = getc (fp)) != EOF)
374     {
375       unsigned code1, code2, c1, c2;
376       int idx1, idx2;
377       char buf[256];
378
379       ungetc (c, fp);
380       fgets (buf, 256, fp);
381       if (c != '#')
382         {
383           if (sscanf (buf, "0x%x-0x%x 0x%x", &code1, &code2, &c1) == 3)
384             {
385               idx1 = CODE_POINT_TO_INDEX (charset, code1);
386               if (idx1 >= size)
387                 continue;
388               idx2 = CODE_POINT_TO_INDEX (charset, code2);
389               if (idx2 >= size)
390                 idx2 = size - 1;
391               c2 = c1 + (idx2 - idx1);
392             }
393           else if (sscanf (buf, "0x%x 0x%x", &code1, &c1) == 2)
394             {
395               idx1 = idx2 = CODE_POINT_TO_INDEX (charset, code1);
396               if (idx1 >= size)
397                 continue;
398               c2 = c1;
399             }
400           else
401             continue;
402           if (idx1 >= 0 && idx2 >= 0)
403             {
404               decoder[idx1] = c1;
405               mchartable_set (encoder, c1, (void *) code1);
406               for (idx1++, c1++; idx1 <= idx2; idx1++, c1++)
407                 {
408                   code1 = INDEX_TO_CODE_POINT (charset, idx1);
409                   decoder[idx1] = c1;
410                   mchartable_set (encoder, c1, (void *) code1);
411                 }
412               found++;
413             }
414         }
415     }
416
417   if (! found)
418     {
419       free (decoder);
420       M17N_OBJECT_UNREF (encoder);
421       return NULL;
422     }
423   plist = mplist ();
424   mplist_add (plist, Mt, decoder);
425   mplist_add (plist, Mt, encoder);
426   return plist;
427 }
428
429 static char *
430 gen_database_name (char *buf, MSymbol *tags)
431 {
432   int i;
433
434   strcpy (buf, msymbol_name (tags[0]));
435   for (i = 1; i < 4; i++)
436     {
437       strcat (buf, ", ");
438       strcat (buf, msymbol_name (tags[i]));
439     }
440   return buf;
441 }
442
443 char *
444 find_file (MDatabaseInfo *db_info, struct stat *buf)
445 {
446   MPlist *plist;
447   char path[PATH_MAX + 1];
448
449   MPLIST_DO (plist, mdatabase__dir_list)
450     {
451       MDatabaseInfo *dir_info = MPLIST_VAL (plist);
452
453       if (dir_info->status != MDB_STATUS_DISABLED
454           && GEN_PATH (path, dir_info->filename, dir_info->len,
455                        db_info->filename, db_info->len)
456           && stat (path, buf) == 0)
457         return strdup (path);
458     }
459   return NULL;
460 }
461
462
463 /* Return the absolute file name for DB_INFO->filename.  If BUF is
464    non-NULL, store the result of `stat' call in it.  It returns NULL
465    if no absolute file name was found.  */
466
467 char *
468 get_database_file (MDatabaseInfo *db_info, struct stat *buf)
469 {
470   if (db_info->status == MDB_STATUS_DISABLED)
471     return NULL;
472   if (db_info->absolute_filename)
473     {
474       if (buf)
475         stat (db_info->absolute_filename, buf);
476     }
477   else
478     {
479       struct stat stat_buf;
480       struct stat *statbuf = buf ? buf : &stat_buf;
481
482       db_info->absolute_filename = find_file (db_info, statbuf);
483     }
484
485   return db_info->absolute_filename;
486 }
487
488 static void *
489 load_database (MSymbol *tags, void *extra_info)
490 {
491   MDatabaseInfo *db_info = extra_info;
492   void *value;
493   char *filename = get_database_file (db_info, NULL);
494   FILE *fp;
495
496   if (! filename || ! (fp = fopen (filename, "r")))
497     MERROR (MERROR_DB, NULL);
498
499   if (tags[0] == Mchar_table)
500     value = load_chartable (fp, tags[1]);
501   else if (tags[0] == Mcharset)
502     value = load_charset (fp, tags[1]);
503   else
504     value = mplist__from_file (fp, NULL);
505   fclose (fp);
506
507   if (! value)
508     MERROR (MERROR_DB, NULL);
509   db_info->time = time (NULL);
510   return value;
511 }
512
513
514 /** Return a newly allocated MDatabaseInfo for DIRNAME.  */
515
516 static MDatabaseInfo *
517 get_dir_info (char *dirname)
518 {
519   MDatabaseInfo *dir_info;
520
521   MSTRUCT_CALLOC (dir_info, MERROR_DB);
522   if (dirname)
523     {
524       int len = strlen (dirname);
525       MTABLE_MALLOC (dir_info->filename, len + 2, MERROR_DB);
526       memcpy (dir_info->filename, dirname, len + 1);
527       /* Append PATH_SEPARATOR if DIRNAME doesn't end with it.  */
528       if (dir_info->filename[len - 1] != PATH_SEPARATOR)
529         {
530           dir_info->filename[len] = PATH_SEPARATOR;
531           dir_info->filename[++len] = '\0';
532         }
533       dir_info->len = len;
534       dir_info->status = MDB_STATUS_AUTO;
535     }
536   else
537     dir_info->status = MDB_STATUS_DISABLED;
538   return dir_info;
539 }
540
541 static MDatabase *
542 find_database (MSymbol tags[4])
543 {
544   MPlist *plist;
545   int i;
546   
547   if (! mdatabase__list)
548     return NULL;
549   for (i = 0, plist = mdatabase__list; i < 4; i++)
550     {
551       plist = mplist__assq (plist, tags[i]);
552       if (! plist)
553         return NULL;
554       plist = MPLIST_PLIST (plist);
555       plist = MPLIST_NEXT (plist);
556     }
557   return MPLIST_VAL (plist);
558 }
559
560 static void
561 free_db_info (MDatabaseInfo *db_info)
562 {
563   free (db_info->filename);
564   if (db_info->absolute_filename
565       && db_info->filename != db_info->absolute_filename)
566     free (db_info->absolute_filename);
567   free (db_info);
568 }
569
570 static MDatabase *
571 register_database (MSymbol tags[4], void *(*loader) (MSymbol *, void *),
572                    void *extra_info, enum MDatabaseStatus status)
573 {
574   MDatabase *mdb = find_database (tags);
575   MDatabaseInfo *db_info = NULL;
576
577   if (mdb)
578     {
579       if (loader == load_database)
580         db_info = mdb->extra_info;
581     }
582   else
583     {
584       int i;
585       MPlist *plist;
586
587       MSTRUCT_MALLOC (mdb, MERROR_DB);
588       for (i = 0; i < 4; i++)
589         mdb->tag[i] = tags[i];
590       mdb->loader = loader;
591       if (loader == load_database)
592         {
593           MSTRUCT_CALLOC (db_info, MERROR_DB);
594           mdb->extra_info = db_info;
595         }
596       else
597         mdb->extra_info = extra_info;
598       if (! mdatabase__list)
599         mdatabase__list = mplist ();
600       for (i = 0, plist = mdatabase__list; i < 4; i++)
601         {
602           MPlist *pl = mplist__assq (plist, tags[i]);
603
604           if (pl)
605             pl = MPLIST_PLIST (pl);
606           else
607             {
608               pl = mplist ();
609               mplist_add (pl, Msymbol, tags[i]);
610               mplist_push (plist, Mplist, pl);
611               M17N_OBJECT_UNREF (pl);
612             }
613           plist = MPLIST_NEXT (pl);
614         }
615       mplist_push (plist, Mt, mdb);
616     }
617
618   if (db_info)
619     {
620       db_info->status = status;
621       if (! db_info->filename
622           || strcmp (db_info->filename, (char *) extra_info) != 0)
623         {
624           if (db_info->filename)
625             free (db_info->filename);
626           if (db_info->absolute_filename
627               && db_info->filename != db_info->absolute_filename)
628             free (db_info->absolute_filename);
629           db_info->filename = strdup ((char *) extra_info);
630           db_info->len = strlen ((char *) extra_info);
631           db_info->time = 0;
632         }
633       if (db_info->filename[0] == PATH_SEPARATOR)
634         db_info->absolute_filename = db_info->filename;
635       else
636         db_info->absolute_filename = NULL;
637     }
638
639   if (mdb->tag[0] == Mchar_table
640       && mdb->tag[2] != Mnil
641       && (mdb->tag[1] == Mstring || mdb->tag[1] == Mtext
642           || mdb->tag[1] == Msymbol || mdb->tag[1] == Minteger
643           || mdb->tag[1] == Mplist))
644     mchar__define_prop (mdb->tag[2], mdb->tag[1], mdb);
645   return mdb;
646 }
647
648 static void
649 register_databases_in_files (MSymbol tags[4], glob_t *globbuf)
650 {
651   int i, j;
652   MPlist *load_key = mplist ();
653   FILE *fp;
654   MPlist *plist;
655
656   for (i = 0; i < globbuf->gl_pathc; i++)
657     {
658       if (! (fp = fopen (globbuf->gl_pathv[i], "r")))
659         continue;
660       plist = mplist__from_file (fp, load_key);
661       fclose (fp);
662       if (! plist)
663         continue;
664       if (MPLIST_PLIST_P (plist))
665         {
666           MPlist *pl;
667           MSymbol tags2[4];
668
669           for (j = 0, pl = MPLIST_PLIST (plist); j < 4 && MPLIST_SYMBOL_P (pl);
670                j++, pl = MPLIST_NEXT (pl))
671             tags2[j] = MPLIST_SYMBOL (pl);
672           for (; j < 4; j++)
673             tags2[j] = Mnil;
674           for (j = 0; j < 4; j++)
675             if (tags[j] == Masterisk ? tags2[j] == Mnil
676                 : (tags[j] != Mnil && tags[j] != tags2[j]))
677               break;
678           if (j == 4)
679             register_database (tags2, load_database, globbuf->gl_pathv[i], 1);
680         }
681       M17N_OBJECT_UNREF (plist);
682     }
683   M17N_OBJECT_UNREF (load_key);
684 }
685
686 \f
687 /* Internal API */
688
689 /** List of database directories.  */ 
690 MPlist *mdatabase__dir_list;
691
692 int
693 mdatabase__init ()
694 {
695   MDatabaseInfo *dir_info;
696   char *path;
697
698   Mchar_table = msymbol ("char-table");
699   Masterisk = msymbol ("*");
700
701   mdatabase__dir_list = mplist ();
702   /** The macro M17NDIR specifies a directory where the system-wide
703     MDB_DIR file exists.  */
704   mplist_set (mdatabase__dir_list, Mt, get_dir_info (M17NDIR));
705
706   /* The variable mdatabase_dir specifies a directory where an
707      application program specific MDB_DIR file exists.  */
708   if (mdatabase_dir && strlen (mdatabase_dir) > 0)
709     mplist_push (mdatabase__dir_list, Mt, get_dir_info (mdatabase_dir));
710
711   /* The environment variable M17NDIR specifies a directory where a
712      user specific MDB_DIR file exists.  */
713   path = getenv ("M17NDIR");
714   if (path && strlen (path) > 0)
715     mplist_push (mdatabase__dir_list, Mt, get_dir_info (path));
716   else
717     {
718       /* If the env var M17NDIR is not set, check "~/.m17n.d".  */
719       char *home = getenv ("HOME");
720       int len;
721
722       if (home
723           && (len = strlen (home))
724           && (path = alloca (len + 9)))
725         {
726           strcpy (path, home);
727           if (path[len - 1] != PATH_SEPARATOR)
728             path[len++] = PATH_SEPARATOR;
729           strcpy (path + len, ".m17n.d");
730           dir_info = get_dir_info (path);
731           mplist_push (mdatabase__dir_list, Mt, dir_info);
732         }
733       else
734         mplist_push (mdatabase__dir_list, Mt, get_dir_info (NULL));
735     }
736
737   mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
738                        mdatabase_find);
739   mdatabase__loader = (void *(*) (void *)) mdatabase_load;
740
741   mdatabase__list = mplist ();
742   mdatabase__update ();
743   return 0;
744 }
745
746 void
747 mdatabase__fini (void)
748 {
749   MPlist *plist, *p0, *p1, *p2, *p3;
750
751   MPLIST_DO (plist, mdatabase__dir_list)
752     free_db_info (MPLIST_VAL (plist));
753   M17N_OBJECT_UNREF (mdatabase__dir_list);
754
755   /* MDATABASE_LIST ::= ((TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) ...) */
756   MPLIST_DO (plist, mdatabase__list)
757     {
758       p0 = MPLIST_PLIST (plist);
759       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) */
760       MPLIST_DO (p0, MPLIST_NEXT (p0))
761         {
762           p1 = MPLIST_PLIST (p0);
763           /* P1 ::= (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) */
764           MPLIST_DO (p1, MPLIST_NEXT (p1))
765             {
766               p2 = MPLIST_PLIST (p1);
767               /* P2 ::= (TAG2 (TAG3 t:MDB) ...) */
768               MPLIST_DO (p2, MPLIST_NEXT (p2))
769                 {
770                   MDatabase *mdb;
771
772                   p3 = MPLIST_PLIST (p2); /* P3 ::= (TAG3 t:MDB) */
773                   p3 = MPLIST_NEXT (p3);
774                   mdb = MPLIST_VAL (p3);
775                   if (mdb->loader == load_database)
776                     free_db_info (mdb->extra_info);
777                   free (mdb);
778                 }
779             }
780         }
781     }
782   M17N_OBJECT_UNREF (mdatabase__list);
783 }
784
785 void
786 mdatabase__update (void)
787 {
788   MPlist *plist, *p0, *p1, *p2, *p3;
789   char path[PATH_MAX + 1];
790   MDatabaseInfo *dir_info;
791   struct stat statbuf;
792
793   /* At first, mark all databases defined automatically from mdb.dir
794      file(s) as "disabled".  */
795   MPLIST_DO (plist, mdatabase__list)
796     {
797       p0 = MPLIST_PLIST (plist);
798       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
799       MPLIST_DO (p0, MPLIST_NEXT (p0))
800         {
801           p1 = MPLIST_PLIST (p0);
802           MPLIST_DO (p1, MPLIST_NEXT (p1))
803             {
804               p2 = MPLIST_PLIST (p1);
805               MPLIST_DO (p2, MPLIST_NEXT (p2))
806                 {
807                   MDatabase *mdb;
808                   MDatabaseInfo *db_info;
809
810                   p3 = MPLIST_PLIST (p2);
811                   p3 = MPLIST_NEXT (p3);
812                   mdb = MPLIST_VAL (p3);
813                   db_info = mdb->extra_info;
814                   if (db_info->status == MDB_STATUS_AUTO)
815                     db_info->status = MDB_STATUS_DISABLED;
816                 }
817             }
818         }
819     }
820
821   /* Update elements of mdatabase__dir_list.  */
822   MPLIST_DO (plist, mdatabase__dir_list)
823     {
824       dir_info = MPLIST_VAL (plist);
825       dir_info->status = ((dir_info->filename
826                            && stat (dir_info->filename, &statbuf) == 0
827                            && (statbuf.st_mode & S_IFDIR))
828                           ? MDB_STATUS_AUTO : MDB_STATUS_DISABLED);
829     }
830
831   MPLIST_DO (plist, mdatabase__dir_list)
832     {
833       MDatabaseInfo *dir_info = MPLIST_VAL (plist);
834       MPlist *pl, *p;
835       int i;
836       FILE *fp;
837
838       if (dir_info->status == MDB_STATUS_DISABLED)
839         continue;
840       if (! GEN_PATH (path, dir_info->filename, dir_info->len,
841                       MDB_DIR, MDB_DIR_LEN))
842         continue;
843       if (stat (path, &statbuf) < 0)
844         continue;
845       if (dir_info->time >= statbuf.st_mtime)
846         continue;
847       dir_info->time = statbuf.st_mtime;
848       if (! (fp = fopen (path, "r")))
849         continue;
850       pl = mplist__from_file (fp, NULL);
851       fclose (fp);
852       if (! pl)
853         continue;
854       MPLIST_DO (p, pl)
855         {
856           MSymbol tags[4];
857           MPlist *p1;
858           MText *mt;
859           int nbytes;
860           int with_wildcard = 0;
861
862           if (! MPLIST_PLIST_P (p))
863             continue;
864           for (i = 0, p1 = MPLIST_PLIST (p); i < 4 && MPLIST_SYMBOL_P (p1);
865                i++, p1 = MPLIST_NEXT (p1))
866             with_wildcard |= ((tags[i] = MPLIST_SYMBOL (p1)) == Masterisk);
867           if (i == 0
868               || tags[0] == Masterisk
869               || ! MPLIST_MTEXT_P (p1))
870             continue;
871           for (; i < 4; i++)
872             tags[i] = Mnil;
873           mt = MPLIST_MTEXT (p1);
874           if (mt->format >= MTEXT_FORMAT_UTF_16LE)
875             mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
876           nbytes = mtext_nbytes (mt);
877           if (nbytes > PATH_MAX)
878             continue;
879           memcpy (path, MTEXT_DATA (mt), nbytes);
880           path[nbytes] = '\0';
881           if (with_wildcard)
882             {
883               glob_t globbuf;
884               MPlist *dlist;
885
886               if (tags[0] == Mchar_table || tags[0] == Mcharset)
887                 continue;
888               if (path[0] == PATH_SEPARATOR)
889                 {
890                   if (glob (path, GLOB_NOSORT, NULL, &globbuf))
891                     continue;
892                   register_databases_in_files (tags, &globbuf);
893                   globfree (&globbuf);
894                 }
895               else
896                 MPLIST_DO (dlist, mdatabase__dir_list)
897                   {
898                     MDatabaseInfo *d_info = MPLIST_VAL (dlist);
899
900                     if (d_info->status == MDB_STATUS_DISABLED)
901                       continue;
902                     if (! GEN_PATH (path, d_info->filename, d_info->len,
903                                     MTEXT_DATA (mt), nbytes))
904                       continue;
905                     if (glob (path, GLOB_NOSORT, NULL, &globbuf))
906                       continue;
907                     register_databases_in_files (tags, &globbuf);
908                     globfree (&globbuf);
909                   }
910             }
911           else
912             {
913               register_database (tags, load_database, path, 1);
914             }
915         }
916       M17N_OBJECT_UNREF (pl);
917     }
918 }
919
920 MPlist *
921 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
922 {
923   int mdebug_mask = MDEBUG_DATABASE;
924   MDatabaseInfo *db_info;
925   char *filename;
926   FILE *fp;
927   MPlist *plist;
928   char name[256];
929
930   if (mdb->loader != load_database
931       || mdb->tag[0] == Mchar_table
932       || mdb->tag[0] == Mcharset)
933     MERROR (MERROR_DB, NULL);
934   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
935                  gen_database_name (name, mdb->tag));
936   db_info = mdb->extra_info;
937   filename = get_database_file (db_info, NULL);
938   if (! filename || ! (fp = fopen (filename, "r")))
939     MERROR (MERROR_DB, NULL);
940   plist = mplist__from_file (fp, keys);
941   fclose (fp);
942   return plist;
943 }
944
945
946 /* Check if the database MDB should be reloaded or not.  It returns:
947
948         1: The database has not been updated since it was loaded last
949         time.
950
951         0: The database has never been loaded or has been updated
952         since it was loaded last time.
953
954         -1: The database is not loadable at the moment.  */
955
956 int
957 mdatabase__check (MDatabase *mdb)
958 {
959   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
960   struct stat buf;
961
962   if (! get_database_file (db_info, &buf))
963     return -1;
964   if (db_info->time < buf.st_mtime)
965     return 0;
966   return 1;
967 }
968
969 /* Search directories in mdatabase__dir_list for file FILENAME.  If
970    the file exist, return the absolute pathname.  If FILENAME is
971    already absolute, return a copy of it.  */
972
973 char *
974 mdatabase__find_file (char *filename)
975 {
976   struct stat buf;
977   MDatabaseInfo db_info;
978
979   if (filename[0] == PATH_SEPARATOR)
980     return (stat (filename, &buf) == 0 ? filename : NULL);
981   db_info.filename = filename;
982   db_info.len = strlen (filename);
983   db_info.time = 0;
984   db_info.absolute_filename = NULL;
985   if (! get_database_file (&db_info, &buf)
986       || stat (db_info.absolute_filename, &buf) < 0)
987     return NULL;
988   return db_info.absolute_filename;
989 }
990
991 char *
992 mdatabase__file (MDatabase *mdb)
993 {
994   MDatabaseInfo *db_info;
995
996   if (mdb->loader != load_database)
997     return NULL;
998   db_info = mdb->extra_info;
999   return get_database_file (db_info, NULL);
1000 }
1001
1002 int
1003 mdatabase__lock (MDatabase *mdb)
1004 {
1005   MDatabaseInfo *db_info;
1006   struct stat buf;
1007   FILE *fp;
1008   int len;
1009   char *file;
1010
1011   if (mdb->loader != load_database)
1012     return -1;
1013   db_info = mdb->extra_info;
1014   if (db_info->lock_file)
1015     return -1;
1016   file = get_database_file (db_info, NULL);
1017   if (! file)
1018     return -1;
1019   len = strlen (file);
1020   db_info->uniq_file = malloc (len + 35);
1021   if (! db_info->uniq_file)
1022     return -1;
1023   db_info->lock_file = malloc (len + 5);
1024   if (! db_info->lock_file)
1025     {
1026       free (db_info->uniq_file);
1027       return -1;
1028     }
1029   sprintf (db_info->uniq_file, "%s.%X.%X", db_info->absolute_filename,
1030            (unsigned) time (NULL), (unsigned) getpid ());
1031   sprintf (db_info->lock_file, "%s.LCK", db_info->absolute_filename);
1032
1033   fp = fopen (db_info->uniq_file, "w");
1034   if (! fp)
1035     {
1036       char *str = strdup (db_info->uniq_file);
1037       char *dir = dirname (str);
1038       
1039       if (stat (dir, &buf) == 0
1040           || mkdir (dir, 0777) < 0
1041           || ! (fp = fopen (db_info->uniq_file, "w")))
1042         {
1043           free (db_info->uniq_file);
1044           free (db_info->lock_file);
1045           db_info->lock_file = NULL;
1046           free (str);
1047           return -1;
1048         }
1049       free (str);
1050     }
1051   fclose (fp);
1052   if (link (db_info->uniq_file, db_info->lock_file) < 0
1053       && (stat (db_info->uniq_file, &buf) < 0
1054           || buf.st_nlink != 2))
1055     {
1056       unlink (db_info->uniq_file);
1057       unlink (db_info->lock_file);
1058       free (db_info->uniq_file);
1059       free (db_info->lock_file);
1060       db_info->lock_file = NULL;
1061       return 0;
1062     }
1063   return 1;
1064 }
1065
1066 int
1067 mdatabase__save (MDatabase *mdb, MPlist *data)
1068 {
1069   MDatabaseInfo *db_info;
1070   FILE *fp;
1071   char *file;
1072   MText *mt;
1073   int ret;
1074
1075   if (mdb->loader != load_database)
1076     return -1;
1077   db_info = mdb->extra_info;
1078   if (! db_info->lock_file)
1079     return -1;
1080   file = get_database_file (db_info, NULL);
1081   if (! file)
1082     return -1;
1083   mt = mtext ();
1084   if (mplist__serialize (mt, data, 1) < 0)
1085     {
1086       M17N_OBJECT_UNREF (mt);
1087       return -1;
1088     }
1089   fp = fopen (db_info->uniq_file, "w");
1090   if (! fp)
1091     {
1092       M17N_OBJECT_UNREF (mt);
1093       return -1;
1094     }
1095   mconv_encode_stream (msymbol ("utf-8"), mt, fp);
1096   M17N_OBJECT_UNREF (mt);
1097   fclose (fp);
1098   if ((ret = rename (db_info->uniq_file, file)) < 0)
1099     unlink (db_info->uniq_file);
1100   free (db_info->uniq_file);
1101   db_info->uniq_file = NULL;
1102   return ret;
1103 }
1104
1105 int
1106 mdatabase__unlock (MDatabase *mdb)
1107 {
1108   MDatabaseInfo *db_info;
1109
1110   if (mdb->loader != load_database)
1111     return -1;
1112   db_info = mdb->extra_info;
1113   if (! db_info->lock_file)
1114     return -1;
1115   unlink (db_info->lock_file);
1116   free (db_info->lock_file);
1117   db_info->lock_file = NULL;
1118   if (db_info->uniq_file)
1119     {
1120       unlink (db_info->uniq_file);
1121       free (db_info->uniq_file);
1122     }
1123   return 0;
1124 }
1125
1126 /*** @} */
1127 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1128
1129 \f
1130 /* External API */
1131
1132 /*** @addtogroup m17nDatabase */
1133 /*** @{ */
1134
1135 /*=*/
1136 /***en
1137     @brief Directory for application specific data.
1138
1139     If an application program wants to provide a data specific to the
1140     program or a data overriding what supplied by the m17n database,
1141     it must set this variable to a name of directory that contains the
1142     data files before it calls the macro M17N_INIT ().  The directory
1143     may contain a file "mdb.dir" which contains a list of data
1144     definitions in the format described in @ref mdbDir "mdbDir(5)".
1145
1146     The default value is NULL.  */
1147 /***ja
1148     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
1149
1150     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n 
1151     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
1152     ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
1153     "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ 
1154     @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
1155
1156     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
1157
1158 char *mdatabase_dir;
1159
1160 /*=*/
1161 /***en
1162     @brief Look for a data in the database.
1163
1164     The mdatabase_find () function searches the m17n database for a
1165     data who has tags $TAG0 through $TAG3, and returns a pointer to
1166     the data.  If such a data is not found, it returns @c NULL.  */
1167
1168 /***ja
1169     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
1170
1171     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
1172     $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
1173     @c NULL ¤òÊÖ¤¹¡£
1174
1175     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
1176
1177 MDatabase *
1178 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1179 {
1180   MSymbol tags[4];
1181
1182   mdatabase__update ();
1183   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1184   return find_database (tags);
1185 }
1186
1187 /*=*/
1188 /***en
1189     @brief Return a data list of the m17n database.
1190
1191     The mdatabase_list () function searches the m17n database for data
1192     who have tags $TAG0 through $TAG3, and returns their list by a
1193     plist.  The value #Mnil in $TAGn means a wild card that matches
1194     any tag.  Each element of the plist has key #Mt and value a
1195     pointer to type #MDatabase.  */
1196 /***ja
1197     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
1198
1199     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
1200     ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
1201     ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
1202     plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
1203
1204 MPlist *
1205 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1206 {
1207   MPlist *plist = mplist (), *pl = plist;
1208   MPlist *p, *p0, *p1, *p2, *p3;
1209
1210   mdatabase__update ();
1211
1212   MPLIST_DO (p, mdatabase__list)
1213     {
1214       p0 = MPLIST_PLIST (p);
1215       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
1216       if (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0)
1217         continue;
1218       MPLIST_DO (p0, MPLIST_NEXT (p0))
1219         {
1220           p1 = MPLIST_PLIST (p0);
1221           if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1)
1222             continue;
1223           MPLIST_DO (p1, MPLIST_NEXT (p1))
1224             {
1225               p2 = MPLIST_PLIST (p1);
1226               if (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2)
1227                 continue;
1228               MPLIST_DO (p2, MPLIST_NEXT (p2))
1229                 {
1230                   p3 = MPLIST_PLIST (p2);
1231                   if (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3)
1232                     continue;
1233                   p3 = MPLIST_NEXT (p3);
1234                   pl = mplist_add (pl, Mt, MPLIST_VAL (p3));
1235                 }
1236             }
1237         }
1238     }
1239   if (MPLIST_TAIL_P (plist))
1240     {
1241       M17N_OBJECT_UNREF (plist);
1242       plist = NULL;
1243     }
1244   return plist;
1245 }
1246
1247 /*=*/
1248 /***en
1249     @brief Define a data of the m17n database.
1250
1251     The mdatabase_define () function defines a data that has tags
1252     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
1253
1254     $LOADER is a pointer to a function that loads the data from the
1255     database.  This function is called from the mdatabase_load ()
1256     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
1257     $TAGS is the array of $TAG0 through $TAG3.
1258
1259     If $LOADER is @c NULL, the default loader of the m17n library is
1260     used.  In this case, $EXTRA_INFO must be a string specifying a
1261     filename that contains the data.
1262
1263     @return
1264     If the operation was successful, mdatabase_define () returns a
1265     pointer to the defined data, which can be used as an argument to
1266     mdatabase_load ().  Otherwise, it returns @c NULL.  */
1267
1268 /***ja
1269     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
1270
1271     ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó 
1272     $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
1273
1274     $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
1275     mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç 
1276     $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1277
1278     ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
1279     $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
1280
1281     @return
1282     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
1283     ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
1284     ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
1285
1286     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
1287
1288 /***
1289     @seealso
1290     mdatabase_load (),  mdatabase_define ()  */
1291
1292 MDatabase *
1293 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
1294                   void *(*loader) (MSymbol *, void *),
1295                   void *extra_info)
1296 {
1297   MDatabase *mdb;
1298   MSymbol tags[4];
1299
1300   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1301   if (! loader)
1302     loader = load_database;
1303   mdb = register_database (tags, loader, extra_info, 0);
1304   return mdb;
1305 }
1306
1307 /*=*/
1308 /***en
1309     @brief Load a data from the database.
1310
1311     The mdatabase_load () function loads a data specified in $MDB and
1312     returns the contents.  The type of contents depends on the type of
1313     the data.
1314
1315     If the data is of the @e plist @e type, this function returns a
1316     pointer to @e plist.
1317
1318     If the database is of the @e chartable @e type, it returns a
1319     chartable.  The default value of the chartable is set according to
1320     the second tag of the data as below:
1321
1322     @li If the tag is #Msymbol, the default value is #Mnil.
1323     @li If the tag is #Minteger, the default value is -1.
1324     @li Otherwise, the default value is @c NULL.
1325
1326     If the data is of the @e charset @e type, it returns a plist of length 2
1327     (keys are both #Mt).  The value of the first element is an array
1328     of integers that maps code points to the corresponding character
1329     codes.  The value of the second element is a chartable of integers
1330     that does the reverse mapping.  The charset must be defined in
1331     advance.  */
1332
1333
1334 /***ja
1335     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
1336
1337     ´Ø¿ô mdatabase_load () ¤Ï $MDB 
1338     ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
1339
1340     ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1341
1342     ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
1343     Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
1344
1345     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
1346     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
1347     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
1348
1349     ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
1350     ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1351     £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
1352     ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
1353
1354     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1355   */
1356
1357 /***
1358     @seealso
1359     mdatabase_load (),  mdatabase_define ()  */
1360
1361 void *
1362 mdatabase_load (MDatabase *mdb)
1363 {
1364   int mdebug_mask = MDEBUG_DATABASE;
1365   char buf[256];
1366
1367   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
1368                  gen_database_name (buf, mdb->tag));
1369   return (*mdb->loader) (mdb->tag, mdb->extra_info);
1370 }
1371
1372 /*=*/
1373 /***en
1374     @brief Get tags of a data.
1375
1376     The mdatabase_tag () function returns an array of tags (symbols)
1377     that identify the data in $MDB.  The length of the array is
1378     four.  */
1379
1380 /***ja
1381     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
1382
1383     ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
1384     4 ¤Ç¤¢¤ë¡£
1385
1386     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
1387
1388 MSymbol *
1389 mdatabase_tag (MDatabase *mdb)
1390 {
1391   return mdb->tag;
1392 }
1393
1394 /*** @} */
1395
1396 /*
1397   Local Variables:
1398   coding: euc-japan
1399   End:
1400 */