(expand_wildcard_database): New function.
[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 static MSymbol Mversion;
164
165 /** Structure for a data in the m17n database.  */
166
167 struct MDatabase
168 {
169   /** Tags to identify the data.  <tag>[0] specifies the type of
170       database.  If it is #Mchar_table, the type is @e chartable, if
171       it is #Mcharset, the type is @e charset, otherwise the type is
172       @e plist.  */
173   MSymbol tag[4];
174
175   void *(*loader) (MSymbol *tags, void *extra_info);
176
177   /** The meaning of the value is dependent on <loader>.  If <loader>
178       is load_database (), the value is a string of the file name that
179       contains the data.  */
180   void *extra_info;
181 };
182
183 static MPlist *mdatabase__list;
184
185 static int
186 read_number (char *buf, int *i)
187 {
188   int idx = *i;
189   int c = buf[idx++];
190   int n;
191
192   if (!c)
193     return -1;
194
195   while (c && isspace (c)) c = buf[idx++];
196
197   if (c == '0')
198     {
199       if (buf[idx] == 'x')
200         {
201           for (idx++, c = 0; (n = hex_mnemonic[(unsigned) buf[idx]]) < 16;
202                idx++)
203             c  = (c << 4) | n;
204           *i = idx;
205           return c;
206         }
207       c = 0;
208     }
209   else if (c == '\'')
210     {
211       c = buf[idx++];
212       if (c == '\\')
213         {
214           c = buf[idx++];
215           n = escape_mnemonic[c];
216           if (n != 255)
217             c = n;
218         }
219       while (buf[idx] && buf[idx++] != '\'');
220       *i = idx;
221       return c;
222     }
223   else if (hex_mnemonic[c] < 10)
224     c -= '0';
225   else
226     return -1;
227
228   while ((n = hex_mnemonic[(unsigned) buf[idx]]) < 10)
229     c = (c * 10) + n, idx++;
230   *i = idx;
231   return c;
232 }
233
234
235 /** Load a data of type @c chartable from the file FD, and return the
236     newly created chartable.  */
237
238 static void *
239 load_chartable (FILE *fp, MSymbol type)
240 {
241   int c, from, to;
242   char buf[1024];
243   void *val;
244   MCharTable *table;
245
246   if (! fp)
247     MERROR (MERROR_DB, NULL);
248
249   table = mchartable (type, (type == Msymbol ? (void *) Mnil
250                              : type == Minteger ? (void *) -1
251                              : NULL));
252
253   while (! feof (fp))
254     {
255       int i, len;
256
257       for (len = 0; len < 1023 && (c = getc (fp)) != EOF && c != '\n'; len++)
258         buf[len] = c;
259       buf[len] = '\0';    
260       if (hex_mnemonic[(unsigned) buf[0]] >= 10)
261         /* skip comment/invalid line */
262         continue;
263       i = 0;
264       from = read_number (buf, &i);
265       if (buf[i] == '-')
266         i++, to = read_number (buf, &i);
267       else
268         to = from;
269       if (from < 0 || to < from)
270         continue;
271
272       while (buf[i] && isspace ((unsigned) buf[i])) i++;
273       c = buf[i];
274       if (!c)
275         continue;
276
277       if (type == Mstring)
278         {
279           /* VAL is a C-string.  */
280           if (! (val = strdup (buf + i)))
281             MEMORY_FULL (MERROR_DB);
282         }
283       else if (type == Minteger)
284         {
285           /* VAL is an integer.  */
286           int positive = 1;
287           int n;
288
289           if (c == '-')
290             i++, positive = -1;
291           n = read_number (buf, &i);
292           if (n < 0)
293             goto label_error;
294           val = (void *) (n * positive);
295         }
296       else if (type == Mtext)
297         {
298           /* VAL is an M-text.  */
299           MText *mt;
300           if (c == '"')
301             mt = mconv_decode_buffer (Mcoding_utf_8,
302                                       (unsigned char *) (buf + i),
303                                       len - i - 1);
304           else
305             {
306               mt = mtext ();
307               while ((c = read_number (buf, &i)) >= 0)
308                 mt = mtext_cat_char (mt, c);
309             }
310           val = (void *) mt;
311         }
312       else if (type == Msymbol)
313         {
314           char *p = buf + i;
315
316           while (*p && ! isspace (*p)) 
317             {
318               if (*p == '\\' && p[1] != '\0')
319                 {
320                   memmove (p, p + 1, buf + len - (p + 1));
321                   len--;
322                 }
323               p++;
324             }
325           *p = '\0';
326           if (! strcmp (buf + i, "nil"))
327             val = (void *) Mnil;
328           else
329             val = (void *) msymbol (buf + i);
330         }
331       else if (type == Mplist)
332         {
333           val = (void *) mplist__from_string ((unsigned char *) buf + i,
334                                               strlen (buf + i));
335         }
336       else
337         val = NULL;
338
339       if (from == to)
340         mchartable_set (table, from, val);
341       else
342         mchartable_set_range (table, from, to, val);
343     }
344   return table;
345
346  label_error:
347   M17N_OBJECT_UNREF (table);
348   MERROR (MERROR_DB, NULL);
349 }
350
351
352 /** Load a data of type @c charset from the file FD.  */
353
354 static void *
355 load_charset (FILE *fp, MSymbol charset_name)
356 {
357   MCharset *charset = MCHARSET (charset_name);
358   int *decoder;
359   MCharTable *encoder;
360   int size;
361   int i, c;
362   int found = 0;
363   MPlist *plist;
364
365   if (! charset)
366     MERROR (MERROR_DB, NULL);
367   size = (charset->code_range[15]
368           - (charset->min_code - charset->code_range_min_code));
369   MTABLE_MALLOC (decoder, size, MERROR_DB);
370   for (i = 0; i < size; i++)
371     decoder[i] = -1;
372   encoder = mchartable (Minteger, (void *) MCHAR_INVALID_CODE);
373
374   while ((c = getc (fp)) != EOF)
375     {
376       unsigned code1, code2, c1, c2;
377       int idx1, idx2;
378       char buf[256];
379
380       ungetc (c, fp);
381       fgets (buf, 256, fp);
382       if (c != '#')
383         {
384           if (sscanf (buf, "0x%x-0x%x 0x%x", &code1, &code2, &c1) == 3)
385             {
386               idx1 = CODE_POINT_TO_INDEX (charset, code1);
387               if (idx1 >= size)
388                 continue;
389               idx2 = CODE_POINT_TO_INDEX (charset, code2);
390               if (idx2 >= size)
391                 idx2 = size - 1;
392               c2 = c1 + (idx2 - idx1);
393             }
394           else if (sscanf (buf, "0x%x 0x%x", &code1, &c1) == 2)
395             {
396               idx1 = idx2 = CODE_POINT_TO_INDEX (charset, code1);
397               if (idx1 >= size)
398                 continue;
399               c2 = c1;
400             }
401           else
402             continue;
403           if (idx1 >= 0 && idx2 >= 0)
404             {
405               decoder[idx1] = c1;
406               mchartable_set (encoder, c1, (void *) code1);
407               for (idx1++, c1++; idx1 <= idx2; idx1++, c1++)
408                 {
409                   code1 = INDEX_TO_CODE_POINT (charset, idx1);
410                   decoder[idx1] = c1;
411                   mchartable_set (encoder, c1, (void *) code1);
412                 }
413               found++;
414             }
415         }
416     }
417
418   if (! found)
419     {
420       free (decoder);
421       M17N_OBJECT_UNREF (encoder);
422       return NULL;
423     }
424   plist = mplist ();
425   mplist_add (plist, Mt, decoder);
426   mplist_add (plist, Mt, encoder);
427   return plist;
428 }
429
430 static char *
431 gen_database_name (char *buf, MSymbol *tags)
432 {
433   int i;
434
435   strcpy (buf, msymbol_name (tags[0]));
436   for (i = 1; i < 4; i++)
437     {
438       strcat (buf, ",");
439       strcat (buf, msymbol_name (tags[i]));
440     }
441   return buf;
442 }
443
444 /* Return the absolute file name for DB_INFO->filename or NULL if no
445    absolute file name was found.  If BUF is non-NULL, store the result
446    of `stat' call in it.  In that case, set *RESULT to the return
447    value of `stat'.  */
448
449 char *
450 get_database_file (MDatabaseInfo *db_info, struct stat *buf, int *result)
451 {
452   if (db_info->absolute_filename)
453     {
454       if (buf)
455         *result = stat (db_info->absolute_filename, buf);
456     }
457   else
458     {
459       struct stat stat_buf;
460       struct stat *statbuf = buf ? buf : &stat_buf;
461       int res;
462       MPlist *plist;
463       char path[PATH_MAX + 1];
464
465       MPLIST_DO (plist, mdatabase__dir_list)
466         {
467           MDatabaseInfo *dir_info = MPLIST_VAL (plist);
468
469           if (dir_info->status != MDB_STATUS_DISABLED
470               && GEN_PATH (path, dir_info->filename, dir_info->len,
471                            db_info->filename, db_info->len)
472               && (res = stat (path, statbuf)) == 0)
473             {
474               db_info->absolute_filename = strdup (path);
475               if (result)
476                 *result = res;
477               break;
478             }
479         }
480     }
481
482   return db_info->absolute_filename;
483 }
484
485 static void *
486 load_database (MSymbol *tags, void *extra_info)
487 {
488   MDatabaseInfo *db_info = extra_info;
489   void *value;
490   char *filename = get_database_file (db_info, NULL, NULL);
491   FILE *fp;
492   int mdebug_mask = MDEBUG_DATABASE;
493   char buf[256];
494
495   MDEBUG_PRINT1 (" [DB] <%s>", gen_database_name (buf, tags));
496   if (! filename || ! (fp = fopen (filename, "r")))
497     {
498       if (filename)
499         MDEBUG_PRINT1 (" open fail: %s\n", filename);
500       else
501         MDEBUG_PRINT1 (" not found: %s\n", db_info->filename);
502       MERROR (MERROR_DB, NULL);
503     }
504
505   MDEBUG_PRINT1 (" from %s\n", filename);
506
507   if (tags[0] == Mchar_table)
508     value = load_chartable (fp, tags[1]);
509   else if (tags[0] == Mcharset)
510     value = load_charset (fp, tags[1]);
511   else
512     value = mplist__from_file (fp, NULL);
513   fclose (fp);
514
515   if (! value)
516     MERROR (MERROR_DB, NULL);
517   db_info->time = time (NULL);
518   return value;
519 }
520
521
522 /** Return a newly allocated MDatabaseInfo for DIRNAME.  */
523
524 static MDatabaseInfo *
525 get_dir_info (char *dirname)
526 {
527   MDatabaseInfo *dir_info;
528
529   MSTRUCT_CALLOC (dir_info, MERROR_DB);
530   if (dirname)
531     {
532       int len = strlen (dirname);
533
534       if (len + MDB_DIR_LEN < PATH_MAX)
535         {
536           MTABLE_MALLOC (dir_info->filename, len + 2, MERROR_DB);
537           memcpy (dir_info->filename, dirname, len + 1);
538           /* Append PATH_SEPARATOR if DIRNAME doesn't end with it.  */
539           if (dir_info->filename[len - 1] != PATH_SEPARATOR)
540             {
541               dir_info->filename[len] = PATH_SEPARATOR;
542               dir_info->filename[++len] = '\0';
543             }
544           dir_info->len = len;
545           dir_info->status = MDB_STATUS_OUTDATED;
546         }
547       else
548         dir_info->status = MDB_STATUS_DISABLED; 
549     }
550   else
551     dir_info->status = MDB_STATUS_DISABLED;
552   return dir_info;
553 }
554
555 static void register_databases_in_files (MSymbol tags[4],
556                                          char *filename, int len);
557
558 static MDatabase *
559 find_database (MSymbol tags[4])
560 {
561   MPlist *plist;
562   int i;
563   MDatabase *mdb;
564   
565   if (! mdatabase__list)
566     return NULL;
567   for (i = 0, plist = mdatabase__list; i < 4; i++)
568     {
569       MPlist *pl = mplist__assq (plist, tags[i]);
570       MPlist *p;
571
572       if ((p = mplist__assq (plist, Masterisk)))
573         {
574           MDatabaseInfo *db_info;
575           int j;
576
577           p = MPLIST_PLIST (p);
578           for (j = i + 1; j < 4; j++)
579             p = MPLIST_PLIST (MPLIST_NEXT (p));
580           mdb = MPLIST_VAL (MPLIST_NEXT (p));
581           db_info = mdb->extra_info;
582           if (db_info->status != MDB_STATUS_DISABLED)
583             {
584               register_databases_in_files (mdb->tag,
585                                            db_info->filename, db_info->len);
586               db_info->status = MDB_STATUS_DISABLED;
587               return find_database (tags);
588             }
589         }
590       if (! pl)
591         return NULL;
592       plist = MPLIST_PLIST (pl);
593       plist = MPLIST_NEXT (plist);
594     }
595   mdb = MPLIST_VAL (plist);
596   return mdb;
597 }
598
599 static void
600 free_db_info (MDatabaseInfo *db_info)
601 {
602   free (db_info->filename);
603   if (db_info->absolute_filename
604       && db_info->filename != db_info->absolute_filename)
605     free (db_info->absolute_filename);
606   M17N_OBJECT_UNREF (db_info->properties);
607   free (db_info);
608 }
609
610 static int
611 check_version (MText *version)
612 {
613   char *verstr = (char *) MTEXT_DATA (version);
614   char *endp = verstr + mtext_nbytes (version);
615   int ver[3];
616   int i;
617
618   ver[0] = ver[1] = ver[2] = 0;
619   for (i = 0; verstr < endp; verstr++)
620     {
621       if (*verstr == '.')
622         {
623           i++;
624           if (i == 3)
625             break;
626           continue;
627         }
628       if (! isdigit (*verstr))
629         break;
630       ver[i] = ver[i] * 10 + (*verstr - '0');
631     }
632   return (ver[0] < M17NLIB_MAJOR_VERSION
633           || (ver[0] == M17NLIB_MAJOR_VERSION
634               && (ver[1] < M17NLIB_MINOR_VERSION
635                   || (ver[1] == M17NLIB_MINOR_VERSION
636                       && ver[2] <= M17NLIB_PATCH_LEVEL))));
637 }
638
639 static MDatabase *
640 register_database (MSymbol tags[4],
641                    void *(*loader) (MSymbol *, void *),
642                    void *extra_info, enum MDatabaseStatus status,
643                    MPlist *properties)
644 {
645   MDatabase *mdb;
646   MDatabaseInfo *db_info;
647   int i;
648   MPlist *plist;
649
650   if (properties)
651     {
652       MPLIST_DO (plist, properties)
653         if (MPLIST_PLIST_P (plist))
654           {
655             MPlist *p = MPLIST_PLIST (plist);
656
657             if (MPLIST_SYMBOL_P (p)
658                 && MPLIST_SYMBOL (p) == Mversion
659                 && MPLIST_MTEXT_P (MPLIST_NEXT (p)))
660               {
661                 if (check_version (MPLIST_MTEXT (MPLIST_NEXT (p))))
662                   break;
663                 return NULL;
664               }
665           }
666     }
667
668   for (i = 0, plist = mdatabase__list; i < 4; i++)
669     {
670       MPlist *pl = mplist__assq (plist, tags[i]);
671
672       if (pl)
673         pl = MPLIST_PLIST (pl);
674       else
675         {
676           pl = mplist ();
677           mplist_add (pl, Msymbol, tags[i]);
678           mplist_push (plist, Mplist, pl);
679           M17N_OBJECT_UNREF (pl);
680         }
681       plist = MPLIST_NEXT (pl);
682     }
683
684   if (MPLIST_TAIL_P (plist))
685     {
686       MSTRUCT_MALLOC (mdb, MERROR_DB);
687       for (i = 0; i < 4; i++)
688         mdb->tag[i] = tags[i];
689       mdb->loader = loader;
690       if (loader == load_database)
691         {
692           MSTRUCT_CALLOC (db_info, MERROR_DB);
693           mdb->extra_info = db_info;
694         }
695       else
696         {
697           db_info = NULL;
698           mdb->extra_info = extra_info;
699         }
700       mplist_push (plist, Mt, mdb);
701     }
702   else
703     {
704       mdb = MPLIST_VAL (plist);
705       if (loader == load_database)
706         db_info = mdb->extra_info;
707       else
708         db_info = NULL;
709     }
710
711   if (db_info)
712     {
713       db_info->status = status;
714       if (! db_info->filename
715           || strcmp (db_info->filename, (char *) extra_info) != 0)
716         {
717           if (db_info->filename)
718             free (db_info->filename);
719           if (db_info->absolute_filename
720               && db_info->filename != db_info->absolute_filename)
721             free (db_info->absolute_filename);
722           db_info->filename = strdup ((char *) extra_info);
723           db_info->len = strlen ((char *) extra_info);
724           db_info->time = 0;
725         }
726       if (db_info->filename[0] == PATH_SEPARATOR)
727         db_info->absolute_filename = db_info->filename;
728       else
729         db_info->absolute_filename = NULL;
730       M17N_OBJECT_UNREF (db_info->properties);
731       if (properties)
732         {
733           db_info->properties = properties;
734           M17N_OBJECT_REF (properties);
735         }
736     }
737
738   if (mdb->tag[0] == Mchar_table
739       && mdb->tag[2] != Mnil
740       && (mdb->tag[1] == Mstring || mdb->tag[1] == Mtext
741           || mdb->tag[1] == Msymbol || mdb->tag[1] == Minteger
742           || mdb->tag[1] == Mplist))
743     mchar__define_prop (mdb->tag[2], mdb->tag[1], mdb);
744   return mdb;
745 }
746
747 static void
748 register_databases_in_files (MSymbol tags[4], char *filename, int len)
749 {
750   int i, j;
751   MPlist *load_key = mplist ();
752   FILE *fp;
753   MPlist *plist, *pl;
754
755   MPLIST_DO (plist, mdatabase__dir_list)
756     {
757       glob_t globbuf;
758       int headlen;
759
760       if (filename[0] == PATH_SEPARATOR)
761         {
762           if (glob (filename, GLOB_NOSORT, NULL, &globbuf))
763             break;
764           headlen = 0;
765         }
766       else
767         {
768           MDatabaseInfo *d_info = MPLIST_VAL (plist);
769           char path[PATH_MAX + 1];
770
771           if (d_info->status == MDB_STATUS_DISABLED)
772             continue;
773           if (! GEN_PATH (path, d_info->filename, d_info->len, filename, len))
774             continue;
775           if (glob (path, GLOB_NOSORT, NULL, &globbuf))
776             continue;
777           headlen = d_info->len;
778         }
779
780       for (i = 0; i < globbuf.gl_pathc; i++)
781         {
782           if (! (fp = fopen (globbuf.gl_pathv[i], "r")))
783             continue;
784           pl = mplist__from_file (fp, load_key);
785           fclose (fp);
786           if (! pl)
787             continue;
788           if (MPLIST_PLIST_P (pl))
789             {
790               MPlist *p;
791               MSymbol tags2[4];
792
793               for (j = 0, p = MPLIST_PLIST (pl); j < 4 && MPLIST_SYMBOL_P (p);
794                    j++, p = MPLIST_NEXT (p))
795                 tags2[j] = MPLIST_SYMBOL (p);
796               for (; j < 4; j++)
797                 tags2[j] = Mnil;
798               for (j = 0; j < 4; j++)
799                 if (tags[j] != Masterisk && tags[j] != tags2[j])
800                   break;
801               if (j == 4)
802                 register_database (tags2, load_database,
803                                    globbuf.gl_pathv[i] + headlen,
804                                    MDB_STATUS_AUTO, p);
805             }
806           M17N_OBJECT_UNREF (pl);
807         }
808       globfree (&globbuf);
809       if (filename[0] == PATH_SEPARATOR)
810         break;
811     }
812   M17N_OBJECT_UNREF (load_key);
813 }
814
815 static int
816 expand_wildcard_database (MPlist *plist)
817 {
818   MDatabase *mdb;
819   MDatabaseInfo *db_info;
820
821   plist = MPLIST_NEXT (plist);
822   while (MPLIST_PLIST_P (plist))
823     {
824       plist = MPLIST_PLIST (plist);
825       plist = MPLIST_NEXT (plist);
826     }
827   mdb = MPLIST_VAL (plist);
828   if (mdb->loader == load_database
829       && (db_info = mdb->extra_info)
830       && db_info->status != MDB_STATUS_DISABLED)
831     {
832       register_databases_in_files (mdb->tag, db_info->filename, db_info->len);
833       db_info->status = MDB_STATUS_DISABLED;
834       return 1;
835     }
836   return 0;
837 }
838
839 \f
840 /* Internal API */
841
842 /** List of database directories.  */ 
843 MPlist *mdatabase__dir_list;
844
845 int
846 mdatabase__init ()
847 {
848   MDatabaseInfo *dir_info;
849   char *path;
850
851   Mchar_table = msymbol ("char-table");
852   Masterisk = msymbol ("*");
853   Mversion = msymbol ("version");
854
855   mdatabase__dir_list = mplist ();
856   /** The macro M17NDIR specifies a directory where the system-wide
857     MDB_DIR file exists.  */
858   mplist_set (mdatabase__dir_list, Mt, get_dir_info (M17NDIR));
859
860   /* The variable mdatabase_dir specifies a directory where an
861      application program specific MDB_DIR file exists.  */
862   if (mdatabase_dir && strlen (mdatabase_dir) > 0)
863     mplist_push (mdatabase__dir_list, Mt, get_dir_info (mdatabase_dir));
864
865   /* The environment variable M17NDIR specifies a directory where a
866      user specific MDB_DIR file exists.  */
867   path = getenv ("M17NDIR");
868   if (path && strlen (path) > 0)
869     mplist_push (mdatabase__dir_list, Mt, get_dir_info (path));
870   else
871     {
872       /* If the env var M17NDIR is not set, check "~/.m17n.d".  */
873       char *home = getenv ("HOME");
874       int len;
875
876       if (home
877           && (len = strlen (home))
878           && (path = alloca (len + 9)))
879         {
880           strcpy (path, home);
881           if (path[len - 1] != PATH_SEPARATOR)
882             path[len++] = PATH_SEPARATOR;
883           strcpy (path + len, ".m17n.d");
884           dir_info = get_dir_info (path);
885           mplist_push (mdatabase__dir_list, Mt, dir_info);
886         }
887       else
888         mplist_push (mdatabase__dir_list, Mt, get_dir_info (NULL));
889     }
890
891   mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
892                        mdatabase_find);
893   mdatabase__loader = (void *(*) (void *)) mdatabase_load;
894
895   mdatabase__list = mplist ();
896   mdatabase__update ();
897   return 0;
898 }
899
900 void
901 mdatabase__fini (void)
902 {
903   MPlist *plist, *p0, *p1, *p2, *p3;
904
905   MPLIST_DO (plist, mdatabase__dir_list)
906     free_db_info (MPLIST_VAL (plist));
907   M17N_OBJECT_UNREF (mdatabase__dir_list);
908
909   /* MDATABASE_LIST ::= ((TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) ...) */
910   MPLIST_DO (plist, mdatabase__list)
911     {
912       p0 = MPLIST_PLIST (plist);
913       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) */
914       MPLIST_DO (p0, MPLIST_NEXT (p0))
915         {
916           p1 = MPLIST_PLIST (p0);
917           /* P1 ::= (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) */
918           MPLIST_DO (p1, MPLIST_NEXT (p1))
919             {
920               p2 = MPLIST_PLIST (p1);
921               /* P2 ::= (TAG2 (TAG3 t:MDB) ...) */
922               MPLIST_DO (p2, MPLIST_NEXT (p2))
923                 {
924                   MDatabase *mdb;
925
926                   p3 = MPLIST_PLIST (p2); /* P3 ::= (TAG3 t:MDB) */
927                   p3 = MPLIST_NEXT (p3);
928                   mdb = MPLIST_VAL (p3);
929                   if (mdb->loader == load_database)
930                     free_db_info (mdb->extra_info);
931                   free (mdb);
932                 }
933             }
934         }
935     }
936   M17N_OBJECT_UNREF (mdatabase__list);
937 }
938
939 void
940 mdatabase__update (void)
941 {
942   MPlist *plist, *p0, *p1, *p2, *p3;
943   char path[PATH_MAX + 1];
944   MDatabaseInfo *dir_info;
945   struct stat statbuf;
946   int rescan = 0;
947
948   /* Update elements of mdatabase__dir_list.  */
949   MPLIST_DO (plist, mdatabase__dir_list)
950     {
951       dir_info = MPLIST_VAL (plist);
952       if (dir_info->filename)
953         {
954           if (stat (dir_info->filename, &statbuf) == 0
955               && (statbuf.st_mode & S_IFDIR))
956             {
957               if (dir_info->time < statbuf.st_mtime)
958                 {
959                   rescan = 1;
960                   dir_info->time = statbuf.st_mtime;
961                 }
962               if (GEN_PATH (path, dir_info->filename, dir_info->len,
963                             MDB_DIR, MDB_DIR_LEN)
964                   && stat (path, &statbuf) >= 0
965                   && dir_info->time < statbuf.st_mtime)
966                 {
967                   rescan = 1;
968                   dir_info->time = statbuf.st_mtime;
969                 }
970               dir_info->status = MDB_STATUS_UPDATED;
971             }
972           else
973             {
974               if (dir_info->status != MDB_STATUS_DISABLED)
975                 {
976                   rescan = 1;
977                   dir_info->time = 0;
978                   dir_info->status = MDB_STATUS_DISABLED;
979                 }
980             }
981         }
982     }
983
984   if (! rescan)
985     return;
986
987   /* At first, mark all databases defined automatically from mdb.dir
988      file(s) as "disabled".  */
989   MPLIST_DO (plist, mdatabase__list)
990     {
991       p0 = MPLIST_PLIST (plist);
992       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
993       MPLIST_DO (p0, MPLIST_NEXT (p0))
994         {
995           p1 = MPLIST_PLIST (p0);
996           MPLIST_DO (p1, MPLIST_NEXT (p1))
997             {
998               p2 = MPLIST_PLIST (p1);
999               MPLIST_DO (p2, MPLIST_NEXT (p2))
1000                 {
1001                   MDatabase *mdb;
1002                   MDatabaseInfo *db_info;
1003
1004                   p3 = MPLIST_PLIST (p2);
1005                   p3 = MPLIST_NEXT (p3);
1006                   mdb = MPLIST_VAL (p3);
1007                   db_info = mdb->extra_info;
1008                   if (db_info->status == MDB_STATUS_AUTO)
1009                     db_info->status = MDB_STATUS_DISABLED;
1010                 }
1011             }
1012         }
1013     }
1014
1015   plist = mplist (); 
1016   MPLIST_DO (p0, mdatabase__dir_list)
1017     mplist_push (plist, MPLIST_KEY (p0), MPLIST_VAL (p0));
1018
1019   while (! MPLIST_TAIL_P (plist))
1020     {
1021       MDatabaseInfo *dir_info = mplist_pop (plist);
1022       MPlist *pl, *p;
1023       int i;
1024       FILE *fp;
1025
1026       if (dir_info->status == MDB_STATUS_DISABLED)
1027         continue;
1028       if (! GEN_PATH (path, dir_info->filename, dir_info->len,
1029                       MDB_DIR, MDB_DIR_LEN))
1030         continue;
1031       if (! (fp = fopen (path, "r")))
1032         continue;
1033       pl = mplist__from_file (fp, NULL);
1034       fclose (fp);
1035       if (! pl)
1036         continue;
1037       MPLIST_DO (p, pl)
1038         {
1039           MSymbol tags[4];
1040           MPlist *p1;
1041           MText *mt;
1042           int nbytes;
1043           int with_wildcard = 0;
1044
1045           if (! MPLIST_PLIST_P (p))
1046             continue;
1047           for (i = 0, p1 = MPLIST_PLIST (p); i < 4 && MPLIST_SYMBOL_P (p1);
1048                i++, p1 = MPLIST_NEXT (p1))
1049             with_wildcard |= ((tags[i] = MPLIST_SYMBOL (p1)) == Masterisk);
1050           if (i == 0
1051               || tags[0] == Masterisk
1052               || ! MPLIST_MTEXT_P (p1))
1053             continue;
1054           for (; i < 4; i++)
1055             tags[i] = with_wildcard ? Masterisk : Mnil;
1056           mt = MPLIST_MTEXT (p1);
1057           nbytes = mtext_nbytes (mt);
1058           if (nbytes > PATH_MAX)
1059             continue;
1060           memcpy (path, MTEXT_DATA (mt), nbytes);
1061           path[nbytes] = '\0';
1062           if (with_wildcard)
1063             register_database (tags, load_database, path,
1064                                MDB_STATUS_AUTO_WILDCARD, NULL);
1065           else
1066             register_database (tags, load_database, path,
1067                                MDB_STATUS_AUTO, p1);
1068         }
1069       M17N_OBJECT_UNREF (pl);
1070     }
1071   M17N_OBJECT_UNREF (plist);
1072 }
1073
1074 MPlist *
1075 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
1076 {
1077   int mdebug_mask = MDEBUG_DATABASE;
1078   MDatabaseInfo *db_info;
1079   char *filename;
1080   FILE *fp;
1081   MPlist *plist;
1082   char name[256];
1083
1084   if (mdb->loader != load_database
1085       || mdb->tag[0] == Mchar_table
1086       || mdb->tag[0] == Mcharset)
1087     MERROR (MERROR_DB, NULL);
1088   MDEBUG_PRINT1 (" [DB]  <%s>.\n",
1089                  gen_database_name (name, mdb->tag));
1090   db_info = mdb->extra_info;
1091   filename = get_database_file (db_info, NULL, NULL);
1092   if (! filename || ! (fp = fopen (filename, "r")))
1093     MERROR (MERROR_DB, NULL);
1094   plist = mplist__from_file (fp, keys);
1095   fclose (fp);
1096   return plist;
1097 }
1098
1099
1100 /* Check if the database MDB should be reloaded or not.  It returns:
1101
1102         1: The database has not been updated since it was loaded last
1103         time.
1104
1105         0: The database has never been loaded or has been updated
1106         since it was loaded last time.
1107
1108         -1: The database is not loadable at the moment.  */
1109
1110 int
1111 mdatabase__check (MDatabase *mdb)
1112 {
1113   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
1114   struct stat buf;
1115   int result;
1116
1117   if (db_info->absolute_filename != db_info->filename
1118       || db_info->status == MDB_STATUS_AUTO)
1119     mdatabase__update ();
1120
1121   if (! get_database_file (db_info, &buf, &result)
1122       || result < 0)
1123     return -1;
1124   if (db_info->time < buf.st_mtime)
1125     return 0;
1126   return 1;
1127 }
1128
1129 /* Search directories in mdatabase__dir_list for file FILENAME.  If
1130    the file exist, return the absolute pathname.  If FILENAME is
1131    already absolute, return a copy of it.  */
1132
1133 char *
1134 mdatabase__find_file (char *filename)
1135 {
1136   struct stat buf;
1137   int result;
1138   MDatabaseInfo db_info;
1139
1140   if (filename[0] == PATH_SEPARATOR)
1141     return (stat (filename, &buf) == 0 ? filename : NULL);
1142   db_info.filename = filename;
1143   db_info.len = strlen (filename);
1144   db_info.time = 0;
1145   db_info.absolute_filename = NULL;
1146   if (! get_database_file (&db_info, &buf, &result)
1147       || result < 0)
1148     return NULL;
1149   return db_info.absolute_filename;
1150 }
1151
1152 char *
1153 mdatabase__file (MDatabase *mdb)
1154 {
1155   MDatabaseInfo *db_info;
1156
1157   if (mdb->loader != load_database)
1158     return NULL;
1159   db_info = mdb->extra_info;
1160   return get_database_file (db_info, NULL, NULL);
1161 }
1162
1163 int
1164 mdatabase__lock (MDatabase *mdb)
1165 {
1166   MDatabaseInfo *db_info;
1167   struct stat buf;
1168   FILE *fp;
1169   int len;
1170   char *file;
1171
1172   if (mdb->loader != load_database)
1173     return -1;
1174   db_info = mdb->extra_info;
1175   if (db_info->lock_file)
1176     return -1;
1177   file = get_database_file (db_info, NULL, NULL);
1178   if (! file)
1179     return -1;
1180   len = strlen (file);
1181   db_info->uniq_file = malloc (len + 35);
1182   if (! db_info->uniq_file)
1183     return -1;
1184   db_info->lock_file = malloc (len + 5);
1185   if (! db_info->lock_file)
1186     {
1187       free (db_info->uniq_file);
1188       return -1;
1189     }
1190   sprintf (db_info->uniq_file, "%s.%X.%X", db_info->absolute_filename,
1191            (unsigned) time (NULL), (unsigned) getpid ());
1192   sprintf (db_info->lock_file, "%s.LCK", db_info->absolute_filename);
1193
1194   fp = fopen (db_info->uniq_file, "w");
1195   if (! fp)
1196     {
1197       char *str = strdup (db_info->uniq_file);
1198       char *dir = dirname (str);
1199       
1200       if (stat (dir, &buf) == 0
1201           || mkdir (dir, 0777) < 0
1202           || ! (fp = fopen (db_info->uniq_file, "w")))
1203         {
1204           free (db_info->uniq_file);
1205           free (db_info->lock_file);
1206           db_info->lock_file = NULL;
1207           free (str);
1208           return -1;
1209         }
1210       free (str);
1211     }
1212   fclose (fp);
1213   if (link (db_info->uniq_file, db_info->lock_file) < 0
1214       && (stat (db_info->uniq_file, &buf) < 0
1215           || buf.st_nlink != 2))
1216     {
1217       unlink (db_info->uniq_file);
1218       unlink (db_info->lock_file);
1219       free (db_info->uniq_file);
1220       free (db_info->lock_file);
1221       db_info->lock_file = NULL;
1222       return 0;
1223     }
1224   return 1;
1225 }
1226
1227 int
1228 mdatabase__save (MDatabase *mdb, MPlist *data)
1229 {
1230   MDatabaseInfo *db_info;
1231   FILE *fp;
1232   char *file;
1233   MText *mt;
1234   int ret;
1235
1236   if (mdb->loader != load_database)
1237     return -1;
1238   db_info = mdb->extra_info;
1239   if (! db_info->lock_file)
1240     return -1;
1241   file = get_database_file (db_info, NULL, NULL);
1242   if (! file)
1243     return -1;
1244   mt = mtext ();
1245   if (mplist__serialize (mt, data, 1) < 0)
1246     {
1247       M17N_OBJECT_UNREF (mt);
1248       return -1;
1249     }
1250   fp = fopen (db_info->uniq_file, "w");
1251   if (! fp)
1252     {
1253       M17N_OBJECT_UNREF (mt);
1254       return -1;
1255     }
1256   mconv_encode_stream (msymbol ("utf-8"), mt, fp);
1257   M17N_OBJECT_UNREF (mt);
1258   fclose (fp);
1259   if ((ret = rename (db_info->uniq_file, file)) < 0)
1260     unlink (db_info->uniq_file);
1261   free (db_info->uniq_file);
1262   db_info->uniq_file = NULL;
1263   return ret;
1264 }
1265
1266 int
1267 mdatabase__unlock (MDatabase *mdb)
1268 {
1269   MDatabaseInfo *db_info;
1270
1271   if (mdb->loader != load_database)
1272     return -1;
1273   db_info = mdb->extra_info;
1274   if (! db_info->lock_file)
1275     return -1;
1276   unlink (db_info->lock_file);
1277   free (db_info->lock_file);
1278   db_info->lock_file = NULL;
1279   if (db_info->uniq_file)
1280     {
1281       unlink (db_info->uniq_file);
1282       free (db_info->uniq_file);
1283     }
1284   return 0;
1285 }
1286
1287 /*** @} */
1288 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1289
1290 \f
1291 /* External API */
1292
1293 /*** @addtogroup m17nDatabase */
1294 /*** @{ */
1295
1296 /*=*/
1297 /***en
1298     @brief Directory for application specific data.
1299
1300     If an application program wants to provide a data specific to the
1301     program or a data overriding what supplied by the m17n database,
1302     it must set this variable to a name of directory that contains the
1303     data files before it calls the macro M17N_INIT ().  The directory
1304     may contain a file "mdb.dir" which contains a list of data
1305     definitions in the format described in @ref mdbDir "mdbDir(5)".
1306
1307     The default value is NULL.  */
1308 /***ja
1309     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
1310
1311     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n 
1312     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
1313     ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
1314     "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ 
1315     @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
1316
1317     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
1318
1319 char *mdatabase_dir;
1320
1321 /*=*/
1322 /***en
1323     @brief Look for a data in the database.
1324
1325     The mdatabase_find () function searches the m17n database for a
1326     data who has tags $TAG0 through $TAG3, and returns a pointer to
1327     the data.  If such a data is not found, it returns @c NULL.  */
1328
1329 /***ja
1330     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
1331
1332     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
1333     $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
1334     @c NULL ¤òÊÖ¤¹¡£
1335
1336     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
1337
1338 MDatabase *
1339 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1340 {
1341   MSymbol tags[4];
1342
1343   mdatabase__update ();
1344   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1345   return find_database (tags);
1346 }
1347
1348 /*=*/
1349 /***en
1350     @brief Return a data list of the m17n database.
1351
1352     The mdatabase_list () function searches the m17n database for data
1353     who have tags $TAG0 through $TAG3, and returns their list by a
1354     plist.  The value #Mnil in $TAGn means a wild card that matches
1355     any tag.  Each element of the plist has key #Mt and value a
1356     pointer to type #MDatabase.  */
1357 /***ja
1358     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
1359
1360     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
1361     ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
1362     ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
1363     plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
1364
1365 MPlist *
1366 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1367 {
1368   MPlist *plist = mplist (), *pl = plist;
1369   MPlist *p, *p0, *p1, *p2, *p3;
1370
1371   mdatabase__update ();
1372
1373   MPLIST_DO (p, mdatabase__list)
1374     {
1375       p0 = MPLIST_PLIST (p);
1376       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
1377       if (MPLIST_SYMBOL (p0) == Masterisk
1378           || (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0))
1379         continue;
1380       MPLIST_DO (p0, MPLIST_NEXT (p0))
1381         {
1382           p1 = MPLIST_PLIST (p0);
1383           if (MPLIST_SYMBOL (p1) == Masterisk)
1384             {
1385               if (expand_wildcard_database (p1))
1386                 {
1387                   M17N_OBJECT_UNREF (plist);
1388                   return mdatabase_list (tag0, tag1, tag2, tag3);
1389                 }
1390               continue;
1391             }
1392           if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1)
1393             continue;
1394           MPLIST_DO (p1, MPLIST_NEXT (p1))
1395             {
1396               p2 = MPLIST_PLIST (p1);
1397               if (MPLIST_SYMBOL (p2) == Masterisk)
1398                 {
1399                   if (expand_wildcard_database (p2))
1400                     {
1401                       M17N_OBJECT_UNREF (plist);
1402                       return mdatabase_list (tag0, tag1, tag2, tag3);
1403                     }
1404                   continue;
1405                 }
1406               if (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2)
1407                 continue;
1408               MPLIST_DO (p2, MPLIST_NEXT (p2))
1409                 {
1410                   p3 = MPLIST_PLIST (p2);
1411                   if (MPLIST_SYMBOL (p3) == Masterisk)
1412                     {
1413                       if (expand_wildcard_database (p3))
1414                         {
1415                           M17N_OBJECT_UNREF (plist);
1416                           return mdatabase_list (tag0, tag1, tag2, tag3);
1417                         }
1418                       continue;
1419                     }
1420                   if (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3)
1421                     continue;
1422                   p3 = MPLIST_NEXT (p3);
1423                   pl = mplist_add (pl, Mt, MPLIST_VAL (p3));
1424                 }
1425             }
1426         }
1427     }
1428   if (MPLIST_TAIL_P (plist))
1429     M17N_OBJECT_UNREF (plist);
1430   return plist;
1431 }
1432
1433 /*=*/
1434 /***en
1435     @brief Define a data of the m17n database.
1436
1437     The mdatabase_define () function defines a data that has tags
1438     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
1439
1440     $LOADER is a pointer to a function that loads the data from the
1441     database.  This function is called from the mdatabase_load ()
1442     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
1443     $TAGS is the array of $TAG0 through $TAG3.
1444
1445     If $LOADER is @c NULL, the default loader of the m17n library is
1446     used.  In this case, $EXTRA_INFO must be a string specifying a
1447     filename that contains the data.
1448
1449     @return
1450     If the operation was successful, mdatabase_define () returns a
1451     pointer to the defined data, which can be used as an argument to
1452     mdatabase_load ().  Otherwise, it returns @c NULL.  */
1453
1454 /***ja
1455     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
1456
1457     ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó 
1458     $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
1459
1460     $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
1461     mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç 
1462     $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1463
1464     ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
1465     $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
1466
1467     @return
1468     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
1469     ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
1470     ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
1471
1472     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
1473
1474 /***
1475     @seealso
1476     mdatabase_load (),  mdatabase_define ()  */
1477
1478 MDatabase *
1479 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
1480                   void *(*loader) (MSymbol *, void *),
1481                   void *extra_info)
1482 {
1483   MDatabase *mdb;
1484   MSymbol tags[4];
1485
1486   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1487   if (! loader)
1488     loader = load_database;
1489   mdb = register_database (tags, loader, extra_info, MDB_STATUS_EXPLICIT, NULL);
1490   return mdb;
1491 }
1492
1493 /*=*/
1494 /***en
1495     @brief Load a data from the database.
1496
1497     The mdatabase_load () function loads a data specified in $MDB and
1498     returns the contents.  The type of contents depends on the type of
1499     the data.
1500
1501     If the data is of the @e plist @e type, this function returns a
1502     pointer to @e plist.
1503
1504     If the database is of the @e chartable @e type, it returns a
1505     chartable.  The default value of the chartable is set according to
1506     the second tag of the data as below:
1507
1508     @li If the tag is #Msymbol, the default value is #Mnil.
1509     @li If the tag is #Minteger, the default value is -1.
1510     @li Otherwise, the default value is @c NULL.
1511
1512     If the data is of the @e charset @e type, it returns a plist of length 2
1513     (keys are both #Mt).  The value of the first element is an array
1514     of integers that maps code points to the corresponding character
1515     codes.  The value of the second element is a chartable of integers
1516     that does the reverse mapping.  The charset must be defined in
1517     advance.  */
1518
1519
1520 /***ja
1521     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
1522
1523     ´Ø¿ô mdatabase_load () ¤Ï $MDB 
1524     ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
1525
1526     ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1527
1528     ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
1529     Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
1530
1531     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
1532     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
1533     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
1534
1535     ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
1536     ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1537     £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
1538     ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
1539
1540     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1541   */
1542
1543 /***
1544     @seealso
1545     mdatabase_load (),  mdatabase_define ()  */
1546
1547 void *
1548 mdatabase_load (MDatabase *mdb)
1549 {
1550   return (*mdb->loader) (mdb->tag, mdb->extra_info);
1551 }
1552
1553 /*=*/
1554 /***en
1555     @brief Get tags of a data.
1556
1557     The mdatabase_tag () function returns an array of tags (symbols)
1558     that identify the data in $MDB.  The length of the array is
1559     four.  */
1560
1561 /***ja
1562     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
1563
1564     ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
1565     4 ¤Ç¤¢¤ë¡£
1566
1567     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
1568
1569 MSymbol *
1570 mdatabase_tag (MDatabase *mdb)
1571 {
1572   return mdb->tag;
1573 }
1574
1575 /*** @} */
1576
1577 /*
1578   Local Variables:
1579   coding: euc-japan
1580   End:
1581 */