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