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