(find_file): Merged into get_database_file.
[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 \f
816 /* Internal API */
817
818 /** List of database directories.  */ 
819 MPlist *mdatabase__dir_list;
820
821 int
822 mdatabase__init ()
823 {
824   MDatabaseInfo *dir_info;
825   char *path;
826
827   Mchar_table = msymbol ("char-table");
828   Masterisk = msymbol ("*");
829   Mversion = msymbol ("version");
830
831   mdatabase__dir_list = mplist ();
832   /** The macro M17NDIR specifies a directory where the system-wide
833     MDB_DIR file exists.  */
834   mplist_set (mdatabase__dir_list, Mt, get_dir_info (M17NDIR));
835
836   /* The variable mdatabase_dir specifies a directory where an
837      application program specific MDB_DIR file exists.  */
838   if (mdatabase_dir && strlen (mdatabase_dir) > 0)
839     mplist_push (mdatabase__dir_list, Mt, get_dir_info (mdatabase_dir));
840
841   /* The environment variable M17NDIR specifies a directory where a
842      user specific MDB_DIR file exists.  */
843   path = getenv ("M17NDIR");
844   if (path && strlen (path) > 0)
845     mplist_push (mdatabase__dir_list, Mt, get_dir_info (path));
846   else
847     {
848       /* If the env var M17NDIR is not set, check "~/.m17n.d".  */
849       char *home = getenv ("HOME");
850       int len;
851
852       if (home
853           && (len = strlen (home))
854           && (path = alloca (len + 9)))
855         {
856           strcpy (path, home);
857           if (path[len - 1] != PATH_SEPARATOR)
858             path[len++] = PATH_SEPARATOR;
859           strcpy (path + len, ".m17n.d");
860           dir_info = get_dir_info (path);
861           mplist_push (mdatabase__dir_list, Mt, dir_info);
862         }
863       else
864         mplist_push (mdatabase__dir_list, Mt, get_dir_info (NULL));
865     }
866
867   mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
868                        mdatabase_find);
869   mdatabase__loader = (void *(*) (void *)) mdatabase_load;
870
871   mdatabase__list = mplist ();
872   mdatabase__update ();
873   return 0;
874 }
875
876 void
877 mdatabase__fini (void)
878 {
879   MPlist *plist, *p0, *p1, *p2, *p3;
880
881   MPLIST_DO (plist, mdatabase__dir_list)
882     free_db_info (MPLIST_VAL (plist));
883   M17N_OBJECT_UNREF (mdatabase__dir_list);
884
885   /* MDATABASE_LIST ::= ((TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) ...) */
886   MPLIST_DO (plist, mdatabase__list)
887     {
888       p0 = MPLIST_PLIST (plist);
889       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) ...) */
890       MPLIST_DO (p0, MPLIST_NEXT (p0))
891         {
892           p1 = MPLIST_PLIST (p0);
893           /* P1 ::= (TAG1 (TAG2 (TAG3 t:MDB) ...) ...) */
894           MPLIST_DO (p1, MPLIST_NEXT (p1))
895             {
896               p2 = MPLIST_PLIST (p1);
897               /* P2 ::= (TAG2 (TAG3 t:MDB) ...) */
898               MPLIST_DO (p2, MPLIST_NEXT (p2))
899                 {
900                   MDatabase *mdb;
901
902                   p3 = MPLIST_PLIST (p2); /* P3 ::= (TAG3 t:MDB) */
903                   p3 = MPLIST_NEXT (p3);
904                   mdb = MPLIST_VAL (p3);
905                   if (mdb->loader == load_database)
906                     free_db_info (mdb->extra_info);
907                   free (mdb);
908                 }
909             }
910         }
911     }
912   M17N_OBJECT_UNREF (mdatabase__list);
913 }
914
915 void
916 mdatabase__update (void)
917 {
918   MPlist *plist, *p0, *p1, *p2, *p3;
919   char path[PATH_MAX + 1];
920   MDatabaseInfo *dir_info;
921   struct stat statbuf;
922   int rescan = 0;
923
924   /* Update elements of mdatabase__dir_list.  */
925   MPLIST_DO (plist, mdatabase__dir_list)
926     {
927       dir_info = MPLIST_VAL (plist);
928       if (dir_info->filename)
929         {
930           if (stat (dir_info->filename, &statbuf) == 0
931               && (statbuf.st_mode & S_IFDIR))
932             {
933               if (dir_info->time < statbuf.st_mtime)
934                 {
935                   rescan = 1;
936                   dir_info->time = statbuf.st_mtime;
937                 }
938               if (GEN_PATH (path, dir_info->filename, dir_info->len,
939                             MDB_DIR, MDB_DIR_LEN)
940                   && stat (path, &statbuf) >= 0
941                   && dir_info->time < statbuf.st_mtime)
942                 {
943                   rescan = 1;
944                   dir_info->time = statbuf.st_mtime;
945                 }
946               dir_info->status = MDB_STATUS_UPDATED;
947             }
948           else
949             {
950               if (dir_info->status != MDB_STATUS_DISABLED)
951                 {
952                   rescan = 1;
953                   dir_info->time = 0;
954                   dir_info->status = MDB_STATUS_DISABLED;
955                 }
956             }
957         }
958     }
959
960   if (! rescan)
961     return;
962
963   /* At first, mark all databases defined automatically from mdb.dir
964      file(s) as "disabled".  */
965   MPLIST_DO (plist, mdatabase__list)
966     {
967       p0 = MPLIST_PLIST (plist);
968       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
969       MPLIST_DO (p0, MPLIST_NEXT (p0))
970         {
971           p1 = MPLIST_PLIST (p0);
972           MPLIST_DO (p1, MPLIST_NEXT (p1))
973             {
974               p2 = MPLIST_PLIST (p1);
975               MPLIST_DO (p2, MPLIST_NEXT (p2))
976                 {
977                   MDatabase *mdb;
978                   MDatabaseInfo *db_info;
979
980                   p3 = MPLIST_PLIST (p2);
981                   p3 = MPLIST_NEXT (p3);
982                   mdb = MPLIST_VAL (p3);
983                   db_info = mdb->extra_info;
984                   if (db_info->status == MDB_STATUS_AUTO)
985                     db_info->status = MDB_STATUS_DISABLED;
986                 }
987             }
988         }
989     }
990
991   plist = mplist (); 
992   MPLIST_DO (p0, mdatabase__dir_list)
993     mplist_push (plist, MPLIST_KEY (p0), MPLIST_VAL (p0));
994
995   while (! MPLIST_TAIL_P (plist))
996     {
997       MDatabaseInfo *dir_info = mplist_pop (plist);
998       MPlist *pl, *p;
999       int i;
1000       FILE *fp;
1001
1002       if (dir_info->status == MDB_STATUS_DISABLED)
1003         continue;
1004       if (! GEN_PATH (path, dir_info->filename, dir_info->len,
1005                       MDB_DIR, MDB_DIR_LEN))
1006         continue;
1007       if (! (fp = fopen (path, "r")))
1008         continue;
1009       pl = mplist__from_file (fp, NULL);
1010       fclose (fp);
1011       if (! pl)
1012         continue;
1013       MPLIST_DO (p, pl)
1014         {
1015           MSymbol tags[4];
1016           MPlist *p1;
1017           MText *mt;
1018           int nbytes;
1019           int with_wildcard = 0;
1020
1021           if (! MPLIST_PLIST_P (p))
1022             continue;
1023           for (i = 0, p1 = MPLIST_PLIST (p); i < 4 && MPLIST_SYMBOL_P (p1);
1024                i++, p1 = MPLIST_NEXT (p1))
1025             with_wildcard |= ((tags[i] = MPLIST_SYMBOL (p1)) == Masterisk);
1026           if (i == 0
1027               || tags[0] == Masterisk
1028               || ! MPLIST_MTEXT_P (p1))
1029             continue;
1030           for (; i < 4; i++)
1031             tags[i] = with_wildcard ? Masterisk : Mnil;
1032           mt = MPLIST_MTEXT (p1);
1033           nbytes = mtext_nbytes (mt);
1034           if (nbytes > PATH_MAX)
1035             continue;
1036           memcpy (path, MTEXT_DATA (mt), nbytes);
1037           path[nbytes] = '\0';
1038           if (with_wildcard)
1039             register_database (tags, load_database, path,
1040                                MDB_STATUS_AUTO_WILDCARD, NULL);
1041           else
1042             register_database (tags, load_database, path,
1043                                MDB_STATUS_AUTO, p1);
1044         }
1045       M17N_OBJECT_UNREF (pl);
1046     }
1047   M17N_OBJECT_UNREF (plist);
1048 }
1049
1050 MPlist *
1051 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
1052 {
1053   int mdebug_mask = MDEBUG_DATABASE;
1054   MDatabaseInfo *db_info;
1055   char *filename;
1056   FILE *fp;
1057   MPlist *plist;
1058   char name[256];
1059
1060   if (mdb->loader != load_database
1061       || mdb->tag[0] == Mchar_table
1062       || mdb->tag[0] == Mcharset)
1063     MERROR (MERROR_DB, NULL);
1064   MDEBUG_PRINT1 (" [DB]  <%s>.\n",
1065                  gen_database_name (name, mdb->tag));
1066   db_info = mdb->extra_info;
1067   filename = get_database_file (db_info, NULL, NULL);
1068   if (! filename || ! (fp = fopen (filename, "r")))
1069     MERROR (MERROR_DB, NULL);
1070   plist = mplist__from_file (fp, keys);
1071   fclose (fp);
1072   return plist;
1073 }
1074
1075
1076 /* Check if the database MDB should be reloaded or not.  It returns:
1077
1078         1: The database has not been updated since it was loaded last
1079         time.
1080
1081         0: The database has never been loaded or has been updated
1082         since it was loaded last time.
1083
1084         -1: The database is not loadable at the moment.  */
1085
1086 int
1087 mdatabase__check (MDatabase *mdb)
1088 {
1089   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
1090   struct stat buf;
1091   int result;
1092
1093   if (db_info->absolute_filename != db_info->filename
1094       || db_info->status == MDB_STATUS_AUTO)
1095     mdatabase__update ();
1096
1097   if (! get_database_file (db_info, &buf, &result)
1098       || result < 0)
1099     return -1;
1100   if (db_info->time < buf.st_mtime)
1101     return 0;
1102   return 1;
1103 }
1104
1105 /* Search directories in mdatabase__dir_list for file FILENAME.  If
1106    the file exist, return the absolute pathname.  If FILENAME is
1107    already absolute, return a copy of it.  */
1108
1109 char *
1110 mdatabase__find_file (char *filename)
1111 {
1112   struct stat buf;
1113   int result;
1114   MDatabaseInfo db_info;
1115
1116   if (filename[0] == PATH_SEPARATOR)
1117     return (stat (filename, &buf) == 0 ? filename : NULL);
1118   db_info.filename = filename;
1119   db_info.len = strlen (filename);
1120   db_info.time = 0;
1121   db_info.absolute_filename = NULL;
1122   if (! get_database_file (&db_info, &buf, &result)
1123       || result < 0)
1124     return NULL;
1125   return db_info.absolute_filename;
1126 }
1127
1128 char *
1129 mdatabase__file (MDatabase *mdb)
1130 {
1131   MDatabaseInfo *db_info;
1132
1133   if (mdb->loader != load_database)
1134     return NULL;
1135   db_info = mdb->extra_info;
1136   return get_database_file (db_info, NULL, NULL);
1137 }
1138
1139 int
1140 mdatabase__lock (MDatabase *mdb)
1141 {
1142   MDatabaseInfo *db_info;
1143   struct stat buf;
1144   FILE *fp;
1145   int len;
1146   char *file;
1147
1148   if (mdb->loader != load_database)
1149     return -1;
1150   db_info = mdb->extra_info;
1151   if (db_info->lock_file)
1152     return -1;
1153   file = get_database_file (db_info, NULL, NULL);
1154   if (! file)
1155     return -1;
1156   len = strlen (file);
1157   db_info->uniq_file = malloc (len + 35);
1158   if (! db_info->uniq_file)
1159     return -1;
1160   db_info->lock_file = malloc (len + 5);
1161   if (! db_info->lock_file)
1162     {
1163       free (db_info->uniq_file);
1164       return -1;
1165     }
1166   sprintf (db_info->uniq_file, "%s.%X.%X", db_info->absolute_filename,
1167            (unsigned) time (NULL), (unsigned) getpid ());
1168   sprintf (db_info->lock_file, "%s.LCK", db_info->absolute_filename);
1169
1170   fp = fopen (db_info->uniq_file, "w");
1171   if (! fp)
1172     {
1173       char *str = strdup (db_info->uniq_file);
1174       char *dir = dirname (str);
1175       
1176       if (stat (dir, &buf) == 0
1177           || mkdir (dir, 0777) < 0
1178           || ! (fp = fopen (db_info->uniq_file, "w")))
1179         {
1180           free (db_info->uniq_file);
1181           free (db_info->lock_file);
1182           db_info->lock_file = NULL;
1183           free (str);
1184           return -1;
1185         }
1186       free (str);
1187     }
1188   fclose (fp);
1189   if (link (db_info->uniq_file, db_info->lock_file) < 0
1190       && (stat (db_info->uniq_file, &buf) < 0
1191           || buf.st_nlink != 2))
1192     {
1193       unlink (db_info->uniq_file);
1194       unlink (db_info->lock_file);
1195       free (db_info->uniq_file);
1196       free (db_info->lock_file);
1197       db_info->lock_file = NULL;
1198       return 0;
1199     }
1200   return 1;
1201 }
1202
1203 int
1204 mdatabase__save (MDatabase *mdb, MPlist *data)
1205 {
1206   MDatabaseInfo *db_info;
1207   FILE *fp;
1208   char *file;
1209   MText *mt;
1210   int ret;
1211
1212   if (mdb->loader != load_database)
1213     return -1;
1214   db_info = mdb->extra_info;
1215   if (! db_info->lock_file)
1216     return -1;
1217   file = get_database_file (db_info, NULL, NULL);
1218   if (! file)
1219     return -1;
1220   mt = mtext ();
1221   if (mplist__serialize (mt, data, 1) < 0)
1222     {
1223       M17N_OBJECT_UNREF (mt);
1224       return -1;
1225     }
1226   fp = fopen (db_info->uniq_file, "w");
1227   if (! fp)
1228     {
1229       M17N_OBJECT_UNREF (mt);
1230       return -1;
1231     }
1232   mconv_encode_stream (msymbol ("utf-8"), mt, fp);
1233   M17N_OBJECT_UNREF (mt);
1234   fclose (fp);
1235   if ((ret = rename (db_info->uniq_file, file)) < 0)
1236     unlink (db_info->uniq_file);
1237   free (db_info->uniq_file);
1238   db_info->uniq_file = NULL;
1239   return ret;
1240 }
1241
1242 int
1243 mdatabase__unlock (MDatabase *mdb)
1244 {
1245   MDatabaseInfo *db_info;
1246
1247   if (mdb->loader != load_database)
1248     return -1;
1249   db_info = mdb->extra_info;
1250   if (! db_info->lock_file)
1251     return -1;
1252   unlink (db_info->lock_file);
1253   free (db_info->lock_file);
1254   db_info->lock_file = NULL;
1255   if (db_info->uniq_file)
1256     {
1257       unlink (db_info->uniq_file);
1258       free (db_info->uniq_file);
1259     }
1260   return 0;
1261 }
1262
1263 /*** @} */
1264 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1265
1266 \f
1267 /* External API */
1268
1269 /*** @addtogroup m17nDatabase */
1270 /*** @{ */
1271
1272 /*=*/
1273 /***en
1274     @brief Directory for application specific data.
1275
1276     If an application program wants to provide a data specific to the
1277     program or a data overriding what supplied by the m17n database,
1278     it must set this variable to a name of directory that contains the
1279     data files before it calls the macro M17N_INIT ().  The directory
1280     may contain a file "mdb.dir" which contains a list of data
1281     definitions in the format described in @ref mdbDir "mdbDir(5)".
1282
1283     The default value is NULL.  */
1284 /***ja
1285     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
1286
1287     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n 
1288     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
1289     ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
1290     "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ 
1291     @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
1292
1293     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
1294
1295 char *mdatabase_dir;
1296
1297 /*=*/
1298 /***en
1299     @brief Look for a data in the database.
1300
1301     The mdatabase_find () function searches the m17n database for a
1302     data who has tags $TAG0 through $TAG3, and returns a pointer to
1303     the data.  If such a data is not found, it returns @c NULL.  */
1304
1305 /***ja
1306     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
1307
1308     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
1309     $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
1310     @c NULL ¤òÊÖ¤¹¡£
1311
1312     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
1313
1314 MDatabase *
1315 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1316 {
1317   MSymbol tags[4];
1318
1319   mdatabase__update ();
1320   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1321   return find_database (tags);
1322 }
1323
1324 /*=*/
1325 /***en
1326     @brief Return a data list of the m17n database.
1327
1328     The mdatabase_list () function searches the m17n database for data
1329     who have tags $TAG0 through $TAG3, and returns their list by a
1330     plist.  The value #Mnil in $TAGn means a wild card that matches
1331     any tag.  Each element of the plist has key #Mt and value a
1332     pointer to type #MDatabase.  */
1333 /***ja
1334     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
1335
1336     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
1337     ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
1338     ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
1339     plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
1340
1341 MPlist *
1342 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1343 {
1344   MPlist *plist = mplist (), *pl = plist;
1345   MPlist *p, *p0, *p1, *p2, *p3;
1346   MDatabase *mdb;
1347   MDatabaseInfo *db_info;
1348
1349   mdatabase__update ();
1350
1351   MPLIST_DO (p, mdatabase__list)
1352     {
1353       p0 = MPLIST_PLIST (p);
1354       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
1355       if (MPLIST_SYMBOL (p0) == Masterisk
1356           || (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0))
1357         continue;
1358       MPLIST_DO (p0, MPLIST_NEXT (p0))
1359         {
1360           p1 = MPLIST_PLIST (p0);
1361           if (MPLIST_SYMBOL (p1) == Masterisk)
1362             {
1363               p1 = MPLIST_PLIST (MPLIST_NEXT (p1));
1364               p1 = MPLIST_PLIST (MPLIST_NEXT (p1));
1365               mdb = MPLIST_VAL (MPLIST_NEXT (p1));
1366               if (mdb->loader == load_database
1367                   && (db_info = mdb->extra_info)
1368                   && db_info->status != MDB_STATUS_DISABLED)
1369                 {
1370                   register_databases_in_files (mdb->tag,
1371                                                db_info->filename, db_info->len);
1372                   db_info->status = MDB_STATUS_DISABLED;
1373                   M17N_OBJECT_UNREF (plist);
1374                   return mdatabase_list (tag0, tag1, tag2, tag3);
1375                 }
1376               continue;
1377             }
1378           if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1)
1379             continue;
1380           MPLIST_DO (p1, MPLIST_NEXT (p1))
1381             {
1382               p2 = MPLIST_PLIST (p1);
1383               if (MPLIST_SYMBOL (p2) == Masterisk
1384                   || (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2))
1385                 continue;
1386               MPLIST_DO (p2, MPLIST_NEXT (p2))
1387                 {
1388                   p3 = MPLIST_PLIST (p2);
1389                   if (MPLIST_SYMBOL (p3) == Masterisk
1390                       || (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3))
1391                     continue;
1392                   p3 = MPLIST_NEXT (p3);
1393                   pl = mplist_add (pl, Mt, MPLIST_VAL (p3));
1394                 }
1395             }
1396         }
1397     }
1398   if (MPLIST_TAIL_P (plist))
1399     M17N_OBJECT_UNREF (plist);
1400   return plist;
1401 }
1402
1403 /*=*/
1404 /***en
1405     @brief Define a data of the m17n database.
1406
1407     The mdatabase_define () function defines a data that has tags
1408     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
1409
1410     $LOADER is a pointer to a function that loads the data from the
1411     database.  This function is called from the mdatabase_load ()
1412     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
1413     $TAGS is the array of $TAG0 through $TAG3.
1414
1415     If $LOADER is @c NULL, the default loader of the m17n library is
1416     used.  In this case, $EXTRA_INFO must be a string specifying a
1417     filename that contains the data.
1418
1419     @return
1420     If the operation was successful, mdatabase_define () returns a
1421     pointer to the defined data, which can be used as an argument to
1422     mdatabase_load ().  Otherwise, it returns @c NULL.  */
1423
1424 /***ja
1425     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
1426
1427     ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó 
1428     $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
1429
1430     $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
1431     mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç 
1432     $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1433
1434     ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
1435     $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
1436
1437     @return
1438     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
1439     ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
1440     ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
1441
1442     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
1443
1444 /***
1445     @seealso
1446     mdatabase_load (),  mdatabase_define ()  */
1447
1448 MDatabase *
1449 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
1450                   void *(*loader) (MSymbol *, void *),
1451                   void *extra_info)
1452 {
1453   MDatabase *mdb;
1454   MSymbol tags[4];
1455
1456   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1457   if (! loader)
1458     loader = load_database;
1459   mdb = register_database (tags, loader, extra_info, MDB_STATUS_EXPLICIT, NULL);
1460   return mdb;
1461 }
1462
1463 /*=*/
1464 /***en
1465     @brief Load a data from the database.
1466
1467     The mdatabase_load () function loads a data specified in $MDB and
1468     returns the contents.  The type of contents depends on the type of
1469     the data.
1470
1471     If the data is of the @e plist @e type, this function returns a
1472     pointer to @e plist.
1473
1474     If the database is of the @e chartable @e type, it returns a
1475     chartable.  The default value of the chartable is set according to
1476     the second tag of the data as below:
1477
1478     @li If the tag is #Msymbol, the default value is #Mnil.
1479     @li If the tag is #Minteger, the default value is -1.
1480     @li Otherwise, the default value is @c NULL.
1481
1482     If the data is of the @e charset @e type, it returns a plist of length 2
1483     (keys are both #Mt).  The value of the first element is an array
1484     of integers that maps code points to the corresponding character
1485     codes.  The value of the second element is a chartable of integers
1486     that does the reverse mapping.  The charset must be defined in
1487     advance.  */
1488
1489
1490 /***ja
1491     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
1492
1493     ´Ø¿ô mdatabase_load () ¤Ï $MDB 
1494     ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
1495
1496     ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1497
1498     ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
1499     Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
1500
1501     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
1502     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
1503     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
1504
1505     ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
1506     ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1507     £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
1508     ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
1509
1510     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1511   */
1512
1513 /***
1514     @seealso
1515     mdatabase_load (),  mdatabase_define ()  */
1516
1517 void *
1518 mdatabase_load (MDatabase *mdb)
1519 {
1520   return (*mdb->loader) (mdb->tag, mdb->extra_info);
1521 }
1522
1523 /*=*/
1524 /***en
1525     @brief Get tags of a data.
1526
1527     The mdatabase_tag () function returns an array of tags (symbols)
1528     that identify the data in $MDB.  The length of the array is
1529     four.  */
1530
1531 /***ja
1532     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
1533
1534     ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
1535     4 ¤Ç¤¢¤ë¡£
1536
1537     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
1538
1539 MSymbol *
1540 mdatabase_tag (MDatabase *mdb)
1541 {
1542   return mdb->tag;
1543 }
1544
1545 /*** @} */
1546
1547 /*
1548   Local Variables:
1549   coding: euc-japan
1550   End:
1551 */