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