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