(mdatabase__update): Unref plist at the tail.
[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   M17N_OBJECT_UNREF (plist);
1014 }
1015
1016 MPlist *
1017 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
1018 {
1019   int mdebug_mask = MDEBUG_DATABASE;
1020   MDatabaseInfo *db_info;
1021   char *filename;
1022   FILE *fp;
1023   MPlist *plist;
1024   char name[256];
1025
1026   if (mdb->loader != load_database
1027       || mdb->tag[0] == Mchar_table
1028       || mdb->tag[0] == Mcharset)
1029     MERROR (MERROR_DB, NULL);
1030   MDEBUG_PRINT1 (" [DB]  <%s>.\n",
1031                  gen_database_name (name, mdb->tag));
1032   db_info = mdb->extra_info;
1033   filename = get_database_file (db_info, NULL);
1034   if (! filename || ! (fp = fopen (filename, "r")))
1035     MERROR (MERROR_DB, NULL);
1036   plist = mplist__from_file (fp, keys);
1037   fclose (fp);
1038   return plist;
1039 }
1040
1041
1042 /* Check if the database MDB should be reloaded or not.  It returns:
1043
1044         1: The database has not been updated since it was loaded last
1045         time.
1046
1047         0: The database has never been loaded or has been updated
1048         since it was loaded last time.
1049
1050         -1: The database is not loadable at the moment.  */
1051
1052 int
1053 mdatabase__check (MDatabase *mdb)
1054 {
1055   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
1056   struct stat buf;
1057
1058   if (! get_database_file (db_info, &buf))
1059     return -1;
1060   if (db_info->time < buf.st_mtime)
1061     return 0;
1062   if (db_info->status == MDB_STATUS_AUTO
1063       && db_info->filename != db_info->absolute_filename)
1064     {
1065       MDatabase *new;
1066       
1067       mdatabase__update ();
1068       new = find_database (mdb->tag);
1069       if (new != mdb)
1070         return 0;
1071     }
1072
1073   return 1;
1074 }
1075
1076 /* Search directories in mdatabase__dir_list for file FILENAME.  If
1077    the file exist, return the absolute pathname.  If FILENAME is
1078    already absolute, return a copy of it.  */
1079
1080 char *
1081 mdatabase__find_file (char *filename)
1082 {
1083   struct stat buf;
1084   MDatabaseInfo db_info;
1085
1086   if (filename[0] == PATH_SEPARATOR)
1087     return (stat (filename, &buf) == 0 ? filename : NULL);
1088   db_info.filename = filename;
1089   db_info.len = strlen (filename);
1090   db_info.time = 0;
1091   db_info.absolute_filename = NULL;
1092   if (! get_database_file (&db_info, &buf)
1093       || stat (db_info.absolute_filename, &buf) < 0)
1094     return NULL;
1095   return db_info.absolute_filename;
1096 }
1097
1098 char *
1099 mdatabase__file (MDatabase *mdb)
1100 {
1101   MDatabaseInfo *db_info;
1102
1103   if (mdb->loader != load_database)
1104     return NULL;
1105   db_info = mdb->extra_info;
1106   return get_database_file (db_info, NULL);
1107 }
1108
1109 int
1110 mdatabase__lock (MDatabase *mdb)
1111 {
1112   MDatabaseInfo *db_info;
1113   struct stat buf;
1114   FILE *fp;
1115   int len;
1116   char *file;
1117
1118   if (mdb->loader != load_database)
1119     return -1;
1120   db_info = mdb->extra_info;
1121   if (db_info->lock_file)
1122     return -1;
1123   file = get_database_file (db_info, NULL);
1124   if (! file)
1125     return -1;
1126   len = strlen (file);
1127   db_info->uniq_file = malloc (len + 35);
1128   if (! db_info->uniq_file)
1129     return -1;
1130   db_info->lock_file = malloc (len + 5);
1131   if (! db_info->lock_file)
1132     {
1133       free (db_info->uniq_file);
1134       return -1;
1135     }
1136   sprintf (db_info->uniq_file, "%s.%X.%X", db_info->absolute_filename,
1137            (unsigned) time (NULL), (unsigned) getpid ());
1138   sprintf (db_info->lock_file, "%s.LCK", db_info->absolute_filename);
1139
1140   fp = fopen (db_info->uniq_file, "w");
1141   if (! fp)
1142     {
1143       char *str = strdup (db_info->uniq_file);
1144       char *dir = dirname (str);
1145       
1146       if (stat (dir, &buf) == 0
1147           || mkdir (dir, 0777) < 0
1148           || ! (fp = fopen (db_info->uniq_file, "w")))
1149         {
1150           free (db_info->uniq_file);
1151           free (db_info->lock_file);
1152           db_info->lock_file = NULL;
1153           free (str);
1154           return -1;
1155         }
1156       free (str);
1157     }
1158   fclose (fp);
1159   if (link (db_info->uniq_file, db_info->lock_file) < 0
1160       && (stat (db_info->uniq_file, &buf) < 0
1161           || buf.st_nlink != 2))
1162     {
1163       unlink (db_info->uniq_file);
1164       unlink (db_info->lock_file);
1165       free (db_info->uniq_file);
1166       free (db_info->lock_file);
1167       db_info->lock_file = NULL;
1168       return 0;
1169     }
1170   return 1;
1171 }
1172
1173 int
1174 mdatabase__save (MDatabase *mdb, MPlist *data)
1175 {
1176   MDatabaseInfo *db_info;
1177   FILE *fp;
1178   char *file;
1179   MText *mt;
1180   int ret;
1181
1182   if (mdb->loader != load_database)
1183     return -1;
1184   db_info = mdb->extra_info;
1185   if (! db_info->lock_file)
1186     return -1;
1187   file = get_database_file (db_info, NULL);
1188   if (! file)
1189     return -1;
1190   mt = mtext ();
1191   if (mplist__serialize (mt, data, 1) < 0)
1192     {
1193       M17N_OBJECT_UNREF (mt);
1194       return -1;
1195     }
1196   fp = fopen (db_info->uniq_file, "w");
1197   if (! fp)
1198     {
1199       M17N_OBJECT_UNREF (mt);
1200       return -1;
1201     }
1202   mconv_encode_stream (msymbol ("utf-8"), mt, fp);
1203   M17N_OBJECT_UNREF (mt);
1204   fclose (fp);
1205   if ((ret = rename (db_info->uniq_file, file)) < 0)
1206     unlink (db_info->uniq_file);
1207   free (db_info->uniq_file);
1208   db_info->uniq_file = NULL;
1209   return ret;
1210 }
1211
1212 int
1213 mdatabase__unlock (MDatabase *mdb)
1214 {
1215   MDatabaseInfo *db_info;
1216
1217   if (mdb->loader != load_database)
1218     return -1;
1219   db_info = mdb->extra_info;
1220   if (! db_info->lock_file)
1221     return -1;
1222   unlink (db_info->lock_file);
1223   free (db_info->lock_file);
1224   db_info->lock_file = NULL;
1225   if (db_info->uniq_file)
1226     {
1227       unlink (db_info->uniq_file);
1228       free (db_info->uniq_file);
1229     }
1230   return 0;
1231 }
1232
1233 /*** @} */
1234 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1235
1236 \f
1237 /* External API */
1238
1239 /*** @addtogroup m17nDatabase */
1240 /*** @{ */
1241
1242 /*=*/
1243 /***en
1244     @brief Directory for application specific data.
1245
1246     If an application program wants to provide a data specific to the
1247     program or a data overriding what supplied by the m17n database,
1248     it must set this variable to a name of directory that contains the
1249     data files before it calls the macro M17N_INIT ().  The directory
1250     may contain a file "mdb.dir" which contains a list of data
1251     definitions in the format described in @ref mdbDir "mdbDir(5)".
1252
1253     The default value is NULL.  */
1254 /***ja
1255     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
1256
1257     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n 
1258     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
1259     ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
1260     "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ 
1261     @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
1262
1263     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
1264
1265 char *mdatabase_dir;
1266
1267 /*=*/
1268 /***en
1269     @brief Look for a data in the database.
1270
1271     The mdatabase_find () function searches the m17n database for a
1272     data who has tags $TAG0 through $TAG3, and returns a pointer to
1273     the data.  If such a data is not found, it returns @c NULL.  */
1274
1275 /***ja
1276     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
1277
1278     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
1279     $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
1280     @c NULL ¤òÊÖ¤¹¡£
1281
1282     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
1283
1284 MDatabase *
1285 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1286 {
1287   MSymbol tags[4];
1288
1289   mdatabase__update ();
1290   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1291   return find_database (tags);
1292 }
1293
1294 /*=*/
1295 /***en
1296     @brief Return a data list of the m17n database.
1297
1298     The mdatabase_list () function searches the m17n database for data
1299     who have tags $TAG0 through $TAG3, and returns their list by a
1300     plist.  The value #Mnil in $TAGn means a wild card that matches
1301     any tag.  Each element of the plist has key #Mt and value a
1302     pointer to type #MDatabase.  */
1303 /***ja
1304     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
1305
1306     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
1307     ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
1308     ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
1309     plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
1310
1311 MPlist *
1312 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1313 {
1314   MPlist *plist = mplist (), *pl = plist;
1315   MPlist *p, *p0, *p1, *p2, *p3;
1316
1317   mdatabase__update ();
1318
1319   MPLIST_DO (p, mdatabase__list)
1320     {
1321       p0 = MPLIST_PLIST (p);
1322       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
1323       if (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0)
1324         continue;
1325       MPLIST_DO (p0, MPLIST_NEXT (p0))
1326         {
1327           p1 = MPLIST_PLIST (p0);
1328           if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1)
1329             continue;
1330           MPLIST_DO (p1, MPLIST_NEXT (p1))
1331             {
1332               p2 = MPLIST_PLIST (p1);
1333               if (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2)
1334                 continue;
1335               MPLIST_DO (p2, MPLIST_NEXT (p2))
1336                 {
1337                   p3 = MPLIST_PLIST (p2);
1338                   if (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3)
1339                     continue;
1340                   p3 = MPLIST_NEXT (p3);
1341                   pl = mplist_add (pl, Mt, MPLIST_VAL (p3));
1342                 }
1343             }
1344         }
1345     }
1346   if (MPLIST_TAIL_P (plist))
1347     {
1348       M17N_OBJECT_UNREF (plist);
1349       plist = NULL;
1350     }
1351   return plist;
1352 }
1353
1354 /*=*/
1355 /***en
1356     @brief Define a data of the m17n database.
1357
1358     The mdatabase_define () function defines a data that has tags
1359     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
1360
1361     $LOADER is a pointer to a function that loads the data from the
1362     database.  This function is called from the mdatabase_load ()
1363     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
1364     $TAGS is the array of $TAG0 through $TAG3.
1365
1366     If $LOADER is @c NULL, the default loader of the m17n library is
1367     used.  In this case, $EXTRA_INFO must be a string specifying a
1368     filename that contains the data.
1369
1370     @return
1371     If the operation was successful, mdatabase_define () returns a
1372     pointer to the defined data, which can be used as an argument to
1373     mdatabase_load ().  Otherwise, it returns @c NULL.  */
1374
1375 /***ja
1376     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
1377
1378     ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó 
1379     $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
1380
1381     $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
1382     mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç 
1383     $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1384
1385     ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
1386     $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
1387
1388     @return
1389     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
1390     ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
1391     ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
1392
1393     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
1394
1395 /***
1396     @seealso
1397     mdatabase_load (),  mdatabase_define ()  */
1398
1399 MDatabase *
1400 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
1401                   void *(*loader) (MSymbol *, void *),
1402                   void *extra_info)
1403 {
1404   MDatabase *mdb;
1405   MSymbol tags[4];
1406
1407   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1408   if (! loader)
1409     loader = load_database;
1410   mdb = register_database (tags, loader, extra_info, MDB_STATUS_EXPLICIT);
1411   return mdb;
1412 }
1413
1414 /*=*/
1415 /***en
1416     @brief Load a data from the database.
1417
1418     The mdatabase_load () function loads a data specified in $MDB and
1419     returns the contents.  The type of contents depends on the type of
1420     the data.
1421
1422     If the data is of the @e plist @e type, this function returns a
1423     pointer to @e plist.
1424
1425     If the database is of the @e chartable @e type, it returns a
1426     chartable.  The default value of the chartable is set according to
1427     the second tag of the data as below:
1428
1429     @li If the tag is #Msymbol, the default value is #Mnil.
1430     @li If the tag is #Minteger, the default value is -1.
1431     @li Otherwise, the default value is @c NULL.
1432
1433     If the data is of the @e charset @e type, it returns a plist of length 2
1434     (keys are both #Mt).  The value of the first element is an array
1435     of integers that maps code points to the corresponding character
1436     codes.  The value of the second element is a chartable of integers
1437     that does the reverse mapping.  The charset must be defined in
1438     advance.  */
1439
1440
1441 /***ja
1442     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
1443
1444     ´Ø¿ô mdatabase_load () ¤Ï $MDB 
1445     ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
1446
1447     ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1448
1449     ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
1450     Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
1451
1452     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
1453     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
1454     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
1455
1456     ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
1457     ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1458     £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
1459     ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
1460
1461     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1462   */
1463
1464 /***
1465     @seealso
1466     mdatabase_load (),  mdatabase_define ()  */
1467
1468 void *
1469 mdatabase_load (MDatabase *mdb)
1470 {
1471   return (*mdb->loader) (mdb->tag, mdb->extra_info);
1472 }
1473
1474 /*=*/
1475 /***en
1476     @brief Get tags of a data.
1477
1478     The mdatabase_tag () function returns an array of tags (symbols)
1479     that identify the data in $MDB.  The length of the array is
1480     four.  */
1481
1482 /***ja
1483     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
1484
1485     ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
1486     4 ¤Ç¤¢¤ë¡£
1487
1488     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
1489
1490 MSymbol *
1491 mdatabase_tag (MDatabase *mdb)
1492 {
1493   return mdb->tag;
1494 }
1495
1496 /*** @} */
1497
1498 /*
1499   Local Variables:
1500   coding: euc-japan
1501   End:
1502 */