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