(mdatabase__unlock): Be sure to unlink uniq file.
[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       else
635         db_info->absolute_filename = NULL;
636     }
637
638   if (mdb->tag[0] == Mchar_table
639       && mdb->tag[2] != Mnil
640       && (mdb->tag[1] == Mstring || mdb->tag[1] == Mtext
641           || mdb->tag[1] == Msymbol || mdb->tag[1] == Minteger
642           || mdb->tag[1] == Mplist))
643     mchar__define_prop (mdb->tag[2], mdb->tag[1], mdb);
644   return mdb;
645 }
646
647 static void
648 register_databases_in_files (MSymbol tags[4], glob_t *globbuf)
649 {
650   int i, j;
651   MPlist *load_key = mplist ();
652   FILE *fp;
653   MPlist *plist;
654
655   for (i = 0; i < globbuf->gl_pathc; i++)
656     {
657       if (! (fp = fopen (globbuf->gl_pathv[i], "r")))
658         continue;
659       plist = mplist__from_file (fp, load_key);
660       fclose (fp);
661       if (! plist)
662         continue;
663       if (MPLIST_PLIST_P (plist))
664         {
665           MPlist *pl;
666           MSymbol tags2[4];
667
668           for (j = 0, pl = MPLIST_PLIST (plist); j < 4 && MPLIST_SYMBOL_P (pl);
669                j++, pl = MPLIST_NEXT (pl))
670             tags2[j] = MPLIST_SYMBOL (pl);
671           for (; j < 4; j++)
672             tags2[j] = Mnil;
673           for (j = 0; j < 4; j++)
674             if (tags[j] == Masterisk ? tags2[j] == Mnil
675                 : (tags[j] != Mnil && tags[j] != tags2[j]))
676               break;
677           if (j == 4)
678             register_database (tags2, load_database, globbuf->gl_pathv[i], 1);
679         }
680       M17N_OBJECT_UNREF (plist);
681     }
682   M17N_OBJECT_UNREF (load_key);
683 }
684
685 \f
686 /* Internal API */
687
688 /** List of database directories.  */ 
689 MPlist *mdatabase__dir_list;
690
691 int
692 mdatabase__init ()
693 {
694   MDatabaseInfo *dir_info;
695   char *path;
696
697   Mchar_table = msymbol ("char-table");
698   Masterisk = msymbol ("*");
699
700   mdatabase__dir_list = mplist ();
701   /** The macro M17NDIR specifies a directory where the system-wide
702     MDB_DIR file exists.  */
703   mplist_set (mdatabase__dir_list, Mt, get_dir_info (M17NDIR));
704
705   /* The variable mdatabase_dir specifies a directory where an
706      application program specific MDB_DIR file exists.  */
707   if (mdatabase_dir && strlen (mdatabase_dir) > 0)
708     mplist_push (mdatabase__dir_list, Mt, get_dir_info (mdatabase_dir));
709
710   /* The environment variable M17NDIR specifies a directory where a
711      user specific MDB_DIR file exists.  */
712   path = getenv ("M17NDIR");
713   if (path && strlen (path) > 0)
714     mplist_push (mdatabase__dir_list, Mt, get_dir_info (path));
715   else
716     {
717       /* If the env var M17NDIR is not set, check "~/.m17n.d".  */
718       char *home = getenv ("HOME");
719       int len;
720
721       if (home
722           && (len = strlen (home))
723           && (path = alloca (len + 9)))
724         {
725           strcpy (path, home);
726           if (path[len - 1] != PATH_SEPARATOR)
727             path[len++] = PATH_SEPARATOR;
728           strcpy (path + len, ".m17n.d");
729           dir_info = get_dir_info (path);
730           mplist_push (mdatabase__dir_list, Mt, dir_info);
731         }
732       else
733         mplist_push (mdatabase__dir_list, Mt, get_dir_info (NULL));
734     }
735
736   mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
737                        mdatabase_find);
738   mdatabase__loader = (void *(*) (void *)) mdatabase_load;
739
740   mdatabase__list = mplist ();
741   mdatabase__update ();
742   return 0;
743 }
744
745 void
746 mdatabase__fini (void)
747 {
748   MPlist *plist, *p0, *p1, *p2, *p3;
749
750   MPLIST_DO (plist, mdatabase__dir_list)
751     free_db_info (MPLIST_VAL (plist));
752   M17N_OBJECT_UNREF (mdatabase__dir_list);
753
754   /* MDATABASE_LIST ::= ((TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) ...) */
755   MPLIST_DO (plist, mdatabase__list)
756     {
757       p0 = MPLIST_PLIST (plist);
758       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) */
759       MPLIST_DO (p0, MPLIST_NEXT (p0))
760         {
761           p1 = MPLIST_PLIST (p0);
762           /* P1 ::= (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) */
763           MPLIST_DO (p1, MPLIST_NEXT (p1))
764             {
765               p2 = MPLIST_PLIST (p1);
766               /* P2 ::= (TAG2 (TAG3 t:MDB) ...) */
767               MPLIST_DO (p2, MPLIST_NEXT (p2))
768                 {
769                   MDatabase *mdb;
770
771                   p3 = MPLIST_PLIST (p2); /* P3 ::= (TAG3 t:MDB) */
772                   p3 = MPLIST_NEXT (p3);
773                   mdb = MPLIST_VAL (p3);
774                   if (mdb->loader == load_database)
775                     free_db_info (mdb->extra_info);
776                   free (mdb);
777                 }
778             }
779         }
780     }
781   M17N_OBJECT_UNREF (mdatabase__list);
782 }
783
784 void
785 mdatabase__update (void)
786 {
787   MPlist *plist, *p0, *p1, *p2, *p3;
788   char path[PATH_MAX + 1];
789   MDatabaseInfo *dir_info;
790   struct stat statbuf;
791
792   /* At first, mark all databases defined automatically from mdb.dir
793      file(s) as "disabled".  */
794   MPLIST_DO (plist, mdatabase__list)
795     {
796       p0 = MPLIST_PLIST (plist);
797       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
798       MPLIST_DO (p0, MPLIST_NEXT (p0))
799         {
800           p1 = MPLIST_PLIST (p0);
801           MPLIST_DO (p1, MPLIST_NEXT (p1))
802             {
803               p2 = MPLIST_PLIST (p1);
804               MPLIST_DO (p2, MPLIST_NEXT (p2))
805                 {
806                   MDatabaseInfo *db_info;
807
808                   p3 = MPLIST_PLIST (p2);
809                   p3 = MPLIST_NEXT (p3);
810                   db_info = MPLIST_VAL (p3);
811                   if (db_info->status == MDB_STATUS_AUTO)
812                     db_info->status = MDB_STATUS_DISABLED;
813                 }
814             }
815         }
816     }
817
818   /* Update elements of mdatabase__dir_list.  */
819   MPLIST_DO (plist, mdatabase__dir_list)
820     {
821       dir_info = MPLIST_VAL (plist);
822       dir_info->status = ((dir_info->filename
823                            && stat (dir_info->filename, &statbuf) == 0
824                            && (statbuf.st_mode & S_IFDIR))
825                           ? MDB_STATUS_AUTO : MDB_STATUS_DISABLED);
826     }
827
828   MPLIST_DO (plist, mdatabase__dir_list)
829     {
830       MDatabaseInfo *dir_info = MPLIST_VAL (plist);
831       MPlist *pl, *p;
832       int i;
833       FILE *fp;
834
835       if (dir_info->status == MDB_STATUS_DISABLED)
836         continue;
837       if (! GEN_PATH (path, dir_info->filename, dir_info->len,
838                       MDB_DIR, MDB_DIR_LEN))
839         continue;
840       if (stat (path, &statbuf) < 0)
841         continue;
842       if (dir_info->time >= statbuf.st_mtime)
843         continue;
844       dir_info->time = statbuf.st_mtime;
845       if (! (fp = fopen (path, "r")))
846         continue;
847       pl = mplist__from_file (fp, NULL);
848       fclose (fp);
849       if (! pl)
850         continue;
851       MPLIST_DO (p, pl)
852         {
853           MSymbol tags[4];
854           MPlist *p1;
855           MText *mt;
856           int nbytes;
857           int with_wildcard = 0;
858
859           if (! MPLIST_PLIST_P (p))
860             continue;
861           for (i = 0, p1 = MPLIST_PLIST (p); i < 4 && MPLIST_SYMBOL_P (p1);
862                i++, p1 = MPLIST_NEXT (p1))
863             with_wildcard |= ((tags[i] = MPLIST_SYMBOL (p1)) == Masterisk);
864           if (i == 0
865               || tags[0] == Masterisk
866               || ! MPLIST_MTEXT_P (p1))
867             continue;
868           for (; i < 4; i++)
869             tags[i] = Mnil;
870           mt = MPLIST_MTEXT (p1);
871           if (mt->format >= MTEXT_FORMAT_UTF_16LE)
872             mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
873           nbytes = mtext_nbytes (mt);
874           if (nbytes > PATH_MAX)
875             continue;
876           memcpy (path, MTEXT_DATA (mt), nbytes);
877           path[nbytes] = '\0';
878           if (with_wildcard)
879             {
880               glob_t globbuf;
881               MPlist *dlist;
882
883               if (tags[0] == Mchar_table || tags[0] == Mcharset)
884                 continue;
885               if (path[0] == PATH_SEPARATOR)
886                 {
887                   if (glob (path, GLOB_NOSORT, NULL, &globbuf))
888                     continue;
889                   register_databases_in_files (tags, &globbuf);
890                   globfree (&globbuf);
891                 }
892               else
893                 MPLIST_DO (dlist, mdatabase__dir_list)
894                   {
895                     MDatabaseInfo *d_info = MPLIST_VAL (dlist);
896
897                     if (d_info->status == MDB_STATUS_DISABLED)
898                       continue;
899                     if (! GEN_PATH (path, d_info->filename, d_info->len,
900                                     MTEXT_DATA (mt), nbytes))
901                       continue;
902                     if (glob (path, GLOB_NOSORT, NULL, &globbuf))
903                       continue;
904                     register_databases_in_files (tags, &globbuf);
905                     globfree (&globbuf);
906                   }
907             }
908           else
909             {
910               register_database (tags, load_database, path, 1);
911             }
912         }
913       M17N_OBJECT_UNREF (pl);
914     }
915 }
916
917 MPlist *
918 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
919 {
920   int mdebug_mask = MDEBUG_DATABASE;
921   MDatabaseInfo *db_info;
922   char *filename;
923   FILE *fp;
924   MPlist *plist;
925   char name[256];
926
927   if (mdb->loader != load_database
928       || mdb->tag[0] == Mchar_table
929       || mdb->tag[0] == Mcharset)
930     MERROR (MERROR_DB, NULL);
931   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
932                  gen_database_name (name, mdb->tag));
933   db_info = mdb->extra_info;
934   filename = get_database_file (db_info, NULL);
935   if (! filename || ! (fp = fopen (filename, "r")))
936     MERROR (MERROR_DB, NULL);
937   plist = mplist__from_file (fp, keys);
938   fclose (fp);
939   return plist;
940 }
941
942
943 /* Check if the database MDB should be reloaded or not.  It returns:
944
945         1: The database has not been updated since it was loaded last
946         time.
947
948         0: The database has never been loaded or has been updated
949         since it was loaded last time.
950
951         -1: The database is not loadable at the moment.  */
952
953 int
954 mdatabase__check (MDatabase *mdb)
955 {
956   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
957   struct stat buf;
958
959   if (! get_database_file (db_info, &buf))
960     return -1;
961   if (db_info->time < buf.st_mtime)
962     return 0;
963   return 1;
964 }
965
966 /* Search directories in mdatabase__dir_list for file FILENAME.  If
967    the file exist, return the absolute pathname.  If FILENAME is
968    already absolute, return a copy of it.  */
969
970 char *
971 mdatabase__find_file (char *filename)
972 {
973   struct stat buf;
974   MDatabaseInfo db_info;
975
976   if (filename[0] == PATH_SEPARATOR)
977     return (stat (filename, &buf) == 0 ? filename : NULL);
978   db_info.filename = filename;
979   db_info.len = strlen (filename);
980   db_info.time = 0;
981   db_info.absolute_filename = NULL;
982   if (! get_database_file (&db_info, &buf)
983       || stat (db_info.absolute_filename, &buf) < 0)
984     return NULL;
985   return db_info.absolute_filename;
986 }
987
988 char *
989 mdatabase__file (MDatabase *mdb)
990 {
991   MDatabaseInfo *db_info;
992
993   if (mdb->loader != load_database)
994     return NULL;
995   db_info = mdb->extra_info;
996   return get_database_file (db_info, NULL);
997 }
998
999 int
1000 mdatabase__lock (MDatabase *mdb)
1001 {
1002   MDatabaseInfo *db_info;
1003   struct stat buf;
1004   FILE *fp;
1005   int len;
1006   char *file;
1007
1008   if (mdb->loader != load_database)
1009     return -1;
1010   db_info = mdb->extra_info;
1011   if (db_info->lock_file)
1012     return -1;
1013   file = get_database_file (db_info, NULL);
1014   if (! file)
1015     return -1;
1016   len = strlen (file);
1017   db_info->uniq_file = malloc (len + 35);
1018   if (! db_info->uniq_file)
1019     return -1;
1020   db_info->lock_file = malloc (len + 5);
1021   if (! db_info->lock_file)
1022     {
1023       free (db_info->uniq_file);
1024       return -1;
1025     }
1026   sprintf (db_info->uniq_file, "%s.%X.%X", db_info->absolute_filename,
1027            (unsigned) time (NULL), (unsigned) getpid ());
1028   sprintf (db_info->lock_file, "%s.LCK", db_info->absolute_filename);
1029
1030   fp = fopen (db_info->uniq_file, "w");
1031   if (! fp)
1032     {
1033       free (db_info->uniq_file);
1034       free (db_info->lock_file);
1035       db_info->lock_file = NULL;
1036       return -1;
1037     }
1038   fclose (fp);
1039   if (link (db_info->uniq_file, db_info->lock_file) < 0
1040       && (stat (db_info->uniq_file, &buf) < 0
1041           || buf.st_nlink != 2))
1042     {
1043       unlink (db_info->uniq_file);
1044       unlink (db_info->lock_file);
1045       free (db_info->uniq_file);
1046       free (db_info->lock_file);
1047       db_info->lock_file = NULL;
1048       return 0;
1049     }
1050   return 1;
1051 }
1052
1053 int
1054 mdatabase__save (MDatabase *mdb, MPlist *data)
1055 {
1056   MDatabaseInfo *db_info;
1057   FILE *fp;
1058   char *file;
1059   MText *mt;
1060   int ret;
1061
1062   if (mdb->loader != load_database)
1063     return -1;
1064   db_info = mdb->extra_info;
1065   if (! db_info->lock_file)
1066     return -1;
1067   file = get_database_file (db_info, NULL);
1068   if (! file)
1069     return -1;
1070   mt = mtext ();
1071   if (mplist__serialize (mt, data, 1) < 0)
1072     {
1073       M17N_OBJECT_UNREF (mt);
1074       return -1;
1075     }
1076   fp = fopen (db_info->uniq_file, "w");
1077   if (! fp)
1078     {
1079       M17N_OBJECT_UNREF (mt);
1080       return -1;
1081     }
1082   mconv_encode_stream (msymbol ("utf-8"), mt, fp);
1083   M17N_OBJECT_UNREF (mt);
1084   fclose (fp);
1085   if ((ret = rename (db_info->uniq_file, file)) < 0)
1086     unlink (db_info->uniq_file);
1087   free (db_info->uniq_file);
1088   db_info->uniq_file = NULL;
1089   return ret;
1090 }
1091
1092 int
1093 mdatabase__unlock (MDatabase *mdb)
1094 {
1095   MDatabaseInfo *db_info;
1096
1097   if (mdb->loader != load_database)
1098     return -1;
1099   db_info = mdb->extra_info;
1100   if (! db_info->lock_file)
1101     return -1;
1102   unlink (db_info->lock_file);
1103   free (db_info->lock_file);
1104   db_info->lock_file = NULL;
1105   if (db_info->uniq_file)
1106     {
1107       unlink (db_info->uniq_file);
1108       free (db_info->uniq_file);
1109     }
1110   return 0;
1111 }
1112
1113 /*** @} */
1114 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1115
1116 \f
1117 /* External API */
1118
1119 /*** @addtogroup m17nDatabase */
1120 /*** @{ */
1121
1122 /*=*/
1123 /***en
1124     @brief Directory for application specific data.
1125
1126     If an application program wants to provide a data specific to the
1127     program or a data overriding what supplied by the m17n database,
1128     it must set this variable to a name of directory that contains the
1129     data files before it calls the macro M17N_INIT ().  The directory
1130     may contain a file "mdb.dir" which contains a list of data
1131     definitions in the format described in @ref mdbDir "mdbDir(5)".
1132
1133     The default value is NULL.  */
1134 /***ja
1135     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
1136
1137     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n 
1138     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
1139     ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
1140     "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ 
1141     @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
1142
1143     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
1144
1145 char *mdatabase_dir;
1146
1147 /*=*/
1148 /***en
1149     @brief Look for a data in the database.
1150
1151     The mdatabase_find () function searches the m17n database for a
1152     data who has tags $TAG0 through $TAG3, and returns a pointer to
1153     the data.  If such a data is not found, it returns @c NULL.  */
1154
1155 /***ja
1156     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
1157
1158     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
1159     $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
1160     @c NULL ¤òÊÖ¤¹¡£
1161
1162     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
1163
1164 MDatabase *
1165 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1166 {
1167   MSymbol tags[4];
1168
1169   mdatabase__update ();
1170   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1171   return find_database (tags);
1172 }
1173
1174 /*=*/
1175 /***en
1176     @brief Return a data list of the m17n database.
1177
1178     The mdatabase_list () function searches the m17n database for data
1179     who have tags $TAG0 through $TAG3, and returns their list by a
1180     plist.  The value #Mnil in $TAGn means a wild card that matches
1181     any tag.  Each element of the plist has key #Mt and value a
1182     pointer to type #MDatabase.  */
1183 /***ja
1184     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
1185
1186     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
1187     ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
1188     ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
1189     plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
1190
1191 MPlist *
1192 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1193 {
1194   MPlist *plist = mplist (), *pl = plist;
1195   MPlist *p, *p0, *p1, *p2, *p3;
1196
1197   mdatabase__update ();
1198
1199   MPLIST_DO (p, mdatabase__list)
1200     {
1201       p0 = MPLIST_PLIST (p);
1202       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
1203       if (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0)
1204         continue;
1205       MPLIST_DO (p0, MPLIST_NEXT (p0))
1206         {
1207           p1 = MPLIST_PLIST (p0);
1208           if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1)
1209             continue;
1210           MPLIST_DO (p1, MPLIST_NEXT (p1))
1211             {
1212               p2 = MPLIST_PLIST (p1);
1213               if (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2)
1214                 continue;
1215               MPLIST_DO (p2, MPLIST_NEXT (p2))
1216                 {
1217                   p3 = MPLIST_PLIST (p2);
1218                   if (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3)
1219                     continue;
1220                   p3 = MPLIST_NEXT (p3);
1221                   pl = mplist_add (pl, Mt, MPLIST_VAL (p3));
1222                 }
1223             }
1224         }
1225     }
1226   if (MPLIST_TAIL_P (plist))
1227     {
1228       M17N_OBJECT_UNREF (plist);
1229       plist = NULL;
1230     }
1231   return plist;
1232 }
1233
1234 /*=*/
1235 /***en
1236     @brief Define a data of the m17n database.
1237
1238     The mdatabase_define () function defines a data that has tags
1239     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
1240
1241     $LOADER is a pointer to a function that loads the data from the
1242     database.  This function is called from the mdatabase_load ()
1243     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
1244     $TAGS is the array of $TAG0 through $TAG3.
1245
1246     If $LOADER is @c NULL, the default loader of the m17n library is
1247     used.  In this case, $EXTRA_INFO must be a string specifying a
1248     filename that contains the data.
1249
1250     @return
1251     If the operation was successful, mdatabase_define () returns a
1252     pointer to the defined data, which can be used as an argument to
1253     mdatabase_load ().  Otherwise, it returns @c NULL.  */
1254
1255 /***ja
1256     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
1257
1258     ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó 
1259     $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
1260
1261     $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
1262     mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç 
1263     $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1264
1265     ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
1266     $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
1267
1268     @return
1269     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
1270     ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
1271     ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
1272
1273     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
1274
1275 /***
1276     @seealso
1277     mdatabase_load (),  mdatabase_define ()  */
1278
1279 MDatabase *
1280 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
1281                   void *(*loader) (MSymbol *, void *),
1282                   void *extra_info)
1283 {
1284   MDatabase *mdb;
1285   MSymbol tags[4];
1286
1287   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1288   if (! loader)
1289     loader = load_database;
1290   mdb = register_database (tags, loader, extra_info, 0);
1291   return mdb;
1292 }
1293
1294 /*=*/
1295 /***en
1296     @brief Load a data from the database.
1297
1298     The mdatabase_load () function loads a data specified in $MDB and
1299     returns the contents.  The type of contents depends on the type of
1300     the data.
1301
1302     If the data is of the @e plist @e type, this function returns a
1303     pointer to @e plist.
1304
1305     If the database is of the @e chartable @e type, it returns a
1306     chartable.  The default value of the chartable is set according to
1307     the second tag of the data as below:
1308
1309     @li If the tag is #Msymbol, the default value is #Mnil.
1310     @li If the tag is #Minteger, the default value is -1.
1311     @li Otherwise, the default value is @c NULL.
1312
1313     If the data is of the @e charset @e type, it returns a plist of length 2
1314     (keys are both #Mt).  The value of the first element is an array
1315     of integers that maps code points to the corresponding character
1316     codes.  The value of the second element is a chartable of integers
1317     that does the reverse mapping.  The charset must be defined in
1318     advance.  */
1319
1320
1321 /***ja
1322     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
1323
1324     ´Ø¿ô mdatabase_load () ¤Ï $MDB 
1325     ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
1326
1327     ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1328
1329     ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
1330     Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
1331
1332     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
1333     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
1334     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
1335
1336     ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
1337     ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1338     £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
1339     ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
1340
1341     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1342   */
1343
1344 /***
1345     @seealso
1346     mdatabase_load (),  mdatabase_define ()  */
1347
1348 void *
1349 mdatabase_load (MDatabase *mdb)
1350 {
1351   int mdebug_mask = MDEBUG_DATABASE;
1352   char buf[256];
1353
1354   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
1355                  gen_database_name (buf, mdb->tag));
1356   return (*mdb->loader) (mdb->tag, mdb->extra_info);
1357 }
1358
1359 /*=*/
1360 /***en
1361     @brief Get tags of a data.
1362
1363     The mdatabase_tag () function returns an array of tags (symbols)
1364     that identify the data in $MDB.  The length of the array is
1365     four.  */
1366
1367 /***ja
1368     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
1369
1370     ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
1371     4 ¤Ç¤¢¤ë¡£
1372
1373     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
1374
1375 MSymbol *
1376 mdatabase_tag (MDatabase *mdb)
1377 {
1378   return mdb->tag;
1379 }
1380
1381 /*** @} */
1382
1383 /*
1384   Local Variables:
1385   coding: euc-japan
1386   End:
1387 */