(MDatabaseList): New type.
[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., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 /***en
24     @addtogroup m17nDatabase
25     @brief The m17n database and API for it.
26
27     The m17n library dynamically acquires various kinds of information
28     in need from data in the <i> m17n database</i>.  Application
29     programs can also add/load their original data to/from the m17n
30     database.  The m17n database contains multiple heterogeneous data,
31     and each data is identified by four tags; TAG0, TAG1, TAG2, TAG3.
32     Each tag must be a symbol.
33
34     TAG0 specifies the type of data stored in the database as below.
35
36     @li
37     If TAG0 is #Mchar_table, the data is of the @e chartable @e
38     type and provides information about each character.  In this case,
39     TAG1 specifies the type of the information and must be #Msymbol,
40     #Minteger, #Mstring, #Mtext, or #Mplist.  TAG2 and TAG3 can be any
41     symbols.
42
43     @li
44     If TAG0 is #Mcharset, the data is of the @e charset @e type
45     and provides a decode/encode mapping table for a charset.  In this
46     case, TAG1 must be a symbol representing a charset.  TAG2 and TAG3
47     can be any symbols.
48
49     @li 
50     If TAG0 is neither #Mchar_table nor #Mcharset, the data is of
51     the @e plist @e type.  See the documentation of the 
52     mdatabase_load () function for the details.  
53     In this case, TAG1, TAG2, and TAG3 can be any symbols.
54
55     The notation \<TAG0, TAG1, TAG2, TAG3\> means a data with those
56     tags.
57
58     Application programs first calls the mdatabase_find () function to
59     get a pointer to an object of the type #MDatabase.  That object
60     holds information about the specified data.  When it is
61     successfully returned, the mdatabase_load () function loads the
62     data.  The implementation of the structure #MDatabase is
63     concealed from application programs.
64 */
65
66 /***ja
67     @addtogroup m17nDatabase
68     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤È¤½¤ì¤Ë´Ø¤¹¤ë API.
69
70     m17n ¥é¥¤¥Ö¥é¥ê¤ÏɬÍפ˱þ¤¸¤ÆưŪ¤Ë @e m17n @e ¥Ç¡¼¥¿¥Ù¡¼¥¹ 
71     ¤«¤é¾ðÊó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤âÆȼ«¤Î¥Ç¡¼¥¿¤ò 
72     m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£m17n 
73     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤ÏÊ£¿ô¤Î¿Íͤʥǡ¼¥¿¤¬´Þ¤Þ¤ì¤Æ¤ª¤ê¡¢³Æ¥Ç¡¼¥¿¤Ï
74     TAG0, TAG1, TAG2, TAG3¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Î£´¤Ä¤Î¥¿¥°¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£
75
76     TAG0 ¤Ë¤è¤Ã¤Æ¡¢¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ï¼¡¤Î¤è¤¦¤Ë»ØÄꤵ¤ì¤ë¡£
77
78     @li 
79     TAG0 ¤¬ #Mchar_table ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e chartable¥¿¥¤¥× 
80     ¤È¸Æ¤Ð¤ì¡¢³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç
81     TAG1 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Msymbol, #Minteger, #Mstring,
82     #Mtext, #Mplist ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
83
84     @li 
85     TAG0 ¤¬ #Mcharset ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e charset¥¿¥¤¥× 
86     ¤È¸Æ¤Ð¤ì¡¢Ê¸»ú¥»¥Ã¥ÈÍѤΥǥ³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥Þ¥Ã¥×¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1
87     ¤Ïʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£TAG2 ¤È TAG3
88     ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
89
90     @li
91     TAG0 ¤¬ #Mchar_table ¤Ç¤â #Mcharset ¤Ç¤â¤Ê¤¤¾ì¹ç¡¢¤½¤Î¥Ç¡¼¥¿¤Ï @e
92     plist¥¿¥¤¥× ¤Ç¤¢¤ë¡£¾ÜºÙ¤Ë´Ø¤·¤Æ¤Ï´Ø¿ô mdatabase_load () 
93     ¤ÎÀâÌÀ¤ò»²¾È¤Î¤³¤È¡£¤³¤Î¾ì¹ç TAG1¡¢TAG2¡¢TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
94
95     ÆÃÄê¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò \<TAG0, TAG1, TAG2, TAG3\> 
96     ¤È¤¤¤¦·Á¼°¤Çɽ¤¹¡£
97
98     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢¤Þ¤º´Ø¿ô mdatabase_find () 
99     ¤ò»È¤Ã¤Æ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÝ»ý¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¡Ê#MDatabase
100     ·¿¡Ë¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÆÀ¤ë¡£¤½¤ì¤ËÀ®¸ù¤·¤¿¤é¡¢ mdatabase_load () 
101     ¤Ë¤è¤Ã¤Æ¼ÂºÝ¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤¹¤ë¡£¹½Â¤ÂΠ#MDatabase 
102     ¼«¿È¤¬¤É¤¦¼ÂÁõ¤µ¤ì¤Æ¤¤¤ë¤«¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£
103
104     @latexonly \IPAlabel{database} @endlatexonly
105 */
106
107 /*=*/
108
109 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
110 /*** @addtogroup m17nInternal
111      @{ */
112
113 #include <config.h>
114 #include <stdio.h>
115 #include <stdlib.h>
116 #include <string.h>
117 #include <ctype.h>
118 #include <sys/types.h>
119 #include <sys/stat.h>
120 #include <unistd.h>
121 #include <limits.h>
122 #include <glob.h>
123
124 #include "m17n.h"
125 #include "m17n-misc.h"
126 #include "internal.h"
127 #include "mtext.h"
128 #include "character.h"
129 #include "charset.h"
130 #include "database.h"
131 #include "coding.h"
132 #include "plist.h"
133
134 /** The file containing a list of databases.  */
135 #define MDB_DIR "mdb.dir"
136 #define MDB_DIR_LEN 8
137
138 #define MAX_TIME(TIME1, TIME2) ((TIME1) >= (TIME2) ? (TIME1) : (TIME2))
139
140 static MSymbol Masterisk;
141
142 /** Structure for a data in the m17n database.  */
143
144 struct MDatabase
145 {
146   /** Tags to identify the data.  <tag>[0] specifies the type of
147       database.  If it is #Mchar_table, the type is @e chartable, if
148       it is #Mcharset, the type is @e charset, otherwise the type is
149       @e plist.  */
150   MSymbol tag[4];
151
152   void *(*loader) (MSymbol *tags, void *extra_info);
153
154   /** The meaning of the value is dependent on <loader>.  If <loader>
155       is load_database (), the value is a string of the file name that
156       contains the data.  */
157   void *extra_info;
158 };
159
160 typedef struct
161 {
162   char *filename;
163   time_t time;
164 } MDatabaseInfo;
165
166 /** List of all data.  */
167 struct MDatabaseList
168 {
169   int size, inc, used;
170   MDatabase *mdbs;
171 };
172
173 static struct MDatabaseList mdb_list;
174
175
176 static int
177 read_number (char *buf, int *i)
178 {
179   int idx = *i;
180   int c = buf[idx++];
181   int n;
182
183   if (!c)
184     return -1;
185
186   while (c && isspace (c)) c = buf[idx++];
187
188   if (c == '0')
189     {
190       if (buf[idx] == 'x')
191         {
192           for (idx++, c = 0; (n = hex_mnemonic[(unsigned) buf[idx]]) < 16;
193                idx++)
194             c  = (c << 4) | n;
195           *i = idx;
196           return c;
197         }
198       c = 0;
199     }
200   else if (c == '\'')
201     {
202       c = buf[idx++];
203       if (c == '\\')
204         {
205           c = buf[idx++];
206           n = escape_mnemonic[c];
207           if (n != 255)
208             c = n;
209         }
210       while (buf[idx] && buf[idx++] != '\'');
211       *i = idx;
212       return c;
213     }
214   else if (hex_mnemonic[c] < 10)
215     c -= '0';
216   else
217     return -1;
218
219   while ((n = hex_mnemonic[(unsigned) buf[idx]]) < 10)
220     c = (c * 10) + n, idx++;
221   *i = idx;
222   return c;
223 }
224
225
226 /** Load a data of type @c chartable from the file FD, and return the
227     newly created chartable.  */
228
229 static void *
230 load_chartable (FILE *fp, MSymbol type)
231 {
232   int c, from, to;
233   char buf[1024];
234   void *val;
235   MCharTable *table;
236
237   if (! fp)
238     MERROR (MERROR_DB, NULL);
239
240   table = mchartable (type, (type == Msymbol ? (void *) Mnil
241                              : type == Minteger ? (void *) -1
242                              : NULL));
243
244   while (! feof (fp))
245     {
246       int i, len;
247
248       for (len = 0; len < 1023 && (c = getc (fp)) != EOF && c != '\n'; len++)
249         buf[len] = c;
250       buf[len] = '\0';    
251       if (hex_mnemonic[(unsigned) buf[0]] >= 10)
252         /* skip comment/invalid line */
253         continue;
254       i = 0;
255       from = read_number (buf, &i);
256       if (buf[i] == '-')
257         i++, to = read_number (buf, &i);
258       else
259         to = from;
260       if (from < 0 || to < from)
261         continue;
262
263       while (buf[i] && isspace ((unsigned) buf[i])) i++;
264       c = buf[i];
265       if (!c)
266         continue;
267
268       if (type == Mstring)
269         {
270           /* VAL is a C-string.  */
271           if (! (val = strdup (buf + i)))
272             MEMORY_FULL (MERROR_DB);
273         }
274       else if (type == Minteger)
275         {
276           /* VAL is an integer.  */
277           int positive = 1;
278           int n;
279
280           if (c == '-')
281             i++, positive = -1;
282           n = read_number (buf, &i);
283           if (n < 0)
284             goto label_error;
285           val = (void *) (n * positive);
286         }
287       else if (type == Mtext)
288         {
289           /* VAL is an M-text.  */
290           MText *mt;
291           if (c == '"')
292             mt = mconv_decode_buffer (Mcoding_utf_8,
293                                       (unsigned char *) (buf + i),
294                                       len - i - 1);
295           else
296             {
297               mt = mtext ();
298               while ((c = read_number (buf, &i)) >= 0)
299                 mt = mtext_cat_char (mt, c);
300             }
301           val = (void *) mt;
302         }
303       else if (type == Msymbol)
304         {
305           char *p = buf + i;
306
307           while (*p && ! isspace (*p)) 
308             {
309               if (*p == '\\' && p[1] != '\0')
310                 {
311                   memmove (p, p + 1, buf + len - (p + 1));
312                   len--;
313                 }
314               p++;
315             }
316           *p = '\0';
317           if (! strcmp (buf + i, "nil"))
318             val = (void *) Mnil;
319           else
320             val = (void *) msymbol (buf + i);
321         }
322       else if (type == Mplist)
323         {
324           val = (void *) mplist__from_string ((unsigned char *) buf + i,
325                                               strlen (buf + i));
326         }
327       else
328         val = NULL;
329
330       if (from == to)
331         mchartable_set (table, from, val);
332       else
333         mchartable_set_range (table, from, to, val);
334     }
335   return table;
336
337  label_error:
338   M17N_OBJECT_UNREF (table);
339   MERROR (MERROR_DB, NULL);
340 }
341
342
343 /** Load a data of type @c charset from the file FD.  */
344
345 static void *
346 load_charset (FILE *fp, MSymbol charset_name)
347 {
348   MCharset *charset = MCHARSET (charset_name);
349   int *decoder;
350   MCharTable *encoder;
351   int size;
352   int i, c;
353   int found = 0;
354   MPlist *plist;
355
356   if (! charset)
357     MERROR (MERROR_DB, NULL);
358   size = (charset->code_range[15]
359           - (charset->min_code - charset->code_range_min_code));
360   MTABLE_MALLOC (decoder, size, MERROR_DB);
361   for (i = 0; i < size; i++)
362     decoder[i] = -1;
363   encoder = mchartable (Minteger, (void *) MCHAR_INVALID_CODE);
364
365   while ((c = getc (fp)) != EOF)
366     {
367       unsigned code1, code2, c1, c2;
368       int idx1, idx2;
369       char buf[256];
370
371       ungetc (c, fp);
372       fgets (buf, 256, fp);
373       if (c != '#')
374         {
375           if (sscanf (buf, "0x%x-0x%x 0x%x", &code1, &code2, &c1) == 3)
376             {
377               idx1 = CODE_POINT_TO_INDEX (charset, code1);
378               if (idx1 >= size)
379                 continue;
380               idx2 = CODE_POINT_TO_INDEX (charset, code2);
381               if (idx2 >= size)
382                 idx2 = size - 1;
383               c2 = c1 + (idx2 - idx1);
384             }
385           else if (sscanf (buf, "0x%x 0x%x", &code1, &c1) == 2)
386             {
387               idx1 = idx2 = CODE_POINT_TO_INDEX (charset, code1);
388               if (idx1 >= size)
389                 continue;
390               c2 = c1;
391             }
392           else
393             continue;
394           if (idx1 >= 0 && idx2 >= 0)
395             {
396               decoder[idx1] = c1;
397               mchartable_set (encoder, c1, (void *) code1);
398               for (idx1++, c1++; idx1 <= idx2; idx1++, c1++)
399                 {
400                   code1 = INDEX_TO_CODE_POINT (charset, idx1);
401                   decoder[idx1] = c1;
402                   mchartable_set (encoder, c1, (void *) code1);
403                 }
404               found++;
405             }
406         }
407     }
408
409   if (! found)
410     {
411       free (decoder);
412       M17N_OBJECT_UNREF (encoder);
413       return NULL;
414     }
415   plist = mplist ();
416   mplist_add (plist, Mt, decoder);
417   mplist_add (plist, Mt, encoder);
418   return plist;
419 }
420
421 static char *
422 gen_database_name (char *buf, MSymbol *tags)
423 {
424   int i;
425
426   strcpy (buf, msymbol_name (tags[0]));
427   for (i = 1; i < 4; i++)
428     {
429       strcat (buf, ", ");
430       strcat (buf, msymbol_name (tags[i]));
431     }
432   return buf;
433 }
434
435 static FILE *
436 get_database_stream (MDatabaseInfo *db_info)
437 {
438   FILE *fp = NULL;
439   struct stat buf;
440
441   if (db_info->filename[0] == '/')
442     {
443       if (stat (db_info->filename, &buf) == 0
444           && (fp = fopen (db_info->filename, "r")))
445         db_info->time = MAX_TIME (buf.st_mtime, buf.st_ctime);
446     }
447   else
448     {
449       MPlist *plist;
450       char *path;
451       int filelen = strlen (db_info->filename);
452       USE_SAFE_ALLOCA;
453
454       MPLIST_DO (plist, mdatabase__dir_list)
455         {
456           MDatabaseInfo *dir_info = MPLIST_VAL (plist);
457           int require = strlen (dir_info->filename) + filelen + 1;
458
459           SAFE_ALLOCA (path, require);
460           strcpy (path, dir_info->filename);
461           strcat (path, db_info->filename);
462           if (stat (path, &buf) == 0
463               && (fp = fopen (path, "r")))
464             {
465               free (db_info->filename);
466               db_info->filename = strdup (path);
467               db_info->time = MAX_TIME (buf.st_mtime, buf.st_ctime);
468               break;
469             }
470         }
471       SAFE_FREE (path);
472     }
473   return fp;
474 }
475
476 static void *
477 load_database (MSymbol *tags, void *extra_info)
478 {
479   FILE *fp = get_database_stream ((MDatabaseInfo *) extra_info);
480   void *value;
481
482   if (! fp)
483     MERROR (MERROR_DB, NULL);
484
485   if (tags[0] == Mchar_table)
486     value = load_chartable (fp, tags[1]);
487   else if (tags[0] == Mcharset)
488     value = load_charset (fp, tags[1]);
489   else
490     value = mplist__from_file (fp, NULL);
491   fclose (fp);
492
493   if (! value)
494     MERROR (MERROR_DB, NULL);
495   return value;
496 }
497
498
499 /** If DIRNAME is a readable directory, allocate MDatabaseInfo and
500     copy DIRNAME to a newly allocated memory and return it.  If
501     DIRNAME does not end with a slash, append a slash to the new memory.  */
502
503 static MDatabaseInfo *
504 get_dir_info (char *dirname)
505 {
506   struct stat buf;
507   int len;
508   MDatabaseInfo *dir_info;
509
510   if (! dirname
511       || stat (dirname, &buf) < 0
512       || ! (buf.st_mode & S_IFDIR))
513     return NULL;
514
515   MSTRUCT_MALLOC (dir_info, MERROR_DB);
516   len = strlen (dirname);
517   MTABLE_MALLOC (dir_info->filename, len + 2, MERROR_DB);
518   memcpy (dir_info->filename, dirname, len + 1);
519   if (dir_info->filename[len - 1] != '/')
520     {
521       dir_info->filename[len] = '/';
522       dir_info->filename[len + 1] = '\0';
523     }
524   /* Set this to zero so that the first call of update_database_list
525      surely checks this directory.  */
526   dir_info->time = 0;
527   return dir_info;
528 }
529
530 static MDatabase *
531 find_database (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
532 {
533   int i;
534
535   for (i = 0; i < mdb_list.used; i++)
536     {
537       MDatabase *mdb = mdb_list.mdbs + i;
538
539       if (tag0 == mdb->tag[0]
540           && tag1 == mdb->tag[1]
541           && tag2 == mdb->tag[2]
542           && tag3 == mdb->tag[3])
543         return mdb;
544     }
545   return NULL;
546 }
547
548 static void
549 update_database_list ()
550 {
551   MPlist *plist;
552   char *path;
553   USE_SAFE_ALLOCA;
554
555   /* Usually this avoids path to be reallocated.  */
556   SAFE_ALLOCA (path, 256);
557
558   MPLIST_DO (plist, mdatabase__dir_list)
559     {
560       MDatabaseInfo *dir_info = MPLIST_VAL (plist);
561       struct stat statbuf;
562       MPlist *pl, *p;
563       int i, j, len;
564       FILE *fp;
565
566       if (stat (dir_info->filename, &statbuf) < 0)
567         continue;
568       if (dir_info->time >= statbuf.st_ctime
569           && dir_info->time >= statbuf.st_mtime)
570         continue;
571       dir_info->time = MAX_TIME (statbuf.st_ctime, statbuf.st_mtime);
572       len = strlen (dir_info->filename);
573 #ifdef PATH_MAX
574       if (len + MDB_DIR_LEN >= PATH_MAX)
575         continue;
576 #endif  /* PATH_MAX */
577       SAFE_ALLOCA (path, len + MDB_DIR_LEN + 1);
578       memcpy (path, dir_info->filename, len);
579       memcpy (path + len, MDB_DIR, MDB_DIR_LEN);
580       path[len + MDB_DIR_LEN] = '\0';
581       if (! (fp = fopen (path, "r")))
582         continue;
583       pl = mplist__from_file (fp, NULL);
584       fclose (fp);
585       if (! pl)
586         continue;
587       MPLIST_DO (p, pl)
588         {
589           MDatabase mdb;
590           MPlist *p1;
591           MText *mt;
592           int nbytes;
593           int with_wildcard = 0;
594
595           if (! MPLIST_PLIST_P (p))
596             continue;
597           p1 = MPLIST_PLIST (p);
598           if (! MPLIST_SYMBOL_P (p1))
599               continue;
600           for (i = 0, p1 = MPLIST_PLIST (p);
601                i < 4 && MPLIST_KEY (p1) == Msymbol;
602                i++, p1 = MPLIST_NEXT (p1))
603             with_wildcard |= ((mdb.tag[i] = MPLIST_SYMBOL (p1)) == Masterisk);
604           if (i == 0
605               || mdb.tag[0] == Masterisk
606               || ! MPLIST_MTEXT_P (p1))
607             continue;
608           for (; i < 4; i++)
609             mdb.tag[i] = Mnil;
610           mdb.loader = load_database;
611           mt = MPLIST_MTEXT (p1);
612           if (mt->format >= MTEXT_FORMAT_UTF_16LE)
613             mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
614           nbytes = mtext_nbytes (mt);
615 #ifdef PATH_MAX
616           if (nbytes > PATH_MAX)
617             continue;
618 #endif  /* PATH_MAX */
619           if (with_wildcard
620               && mdb.tag[0] != Mchar_table
621               && mdb.tag[0] != Mcharset)
622             {
623               glob_t globbuf;
624               MPlist *load_key;
625
626               SAFE_ALLOCA (path, len + nbytes + 1);
627               memcpy (path, dir_info->filename, len);
628               memcpy (path + len, mt->data, nbytes);
629               path[len + nbytes] = '\0';
630
631               if (glob (path, GLOB_NOSORT | GLOB_NOCHECK, NULL, &globbuf) != 0)
632                 continue;
633               load_key = mplist ();
634               for (i = 0; i < globbuf.gl_pathc; i++)
635                 {
636                   if (! (fp = fopen (globbuf.gl_pathv[i], "r")))
637                     continue;
638                   p1 = mplist__from_file (fp, load_key);
639                   fclose (fp);
640                   if (! p1)
641                     continue;
642                   if (MPLIST_PLIST_P (p1))
643                     {
644                       MPlist *p0;
645                       MDatabase mdb2;
646
647                       for (j = 0, p0 = MPLIST_PLIST (p1);
648                            j < 4 && MPLIST_SYMBOL_P (p0);
649                            j++, p0 = MPLIST_NEXT (p0))
650                         mdb2.tag[j] = MPLIST_SYMBOL (p0);
651                       for (; j < 4; j++)
652                         mdb2.tag[j] = Mnil;
653                       for (j = 0; j < 4; j++)
654                         if (mdb.tag[j] == Masterisk
655                             ? mdb2.tag[j] == Mnil
656                             : (mdb.tag[j] != Mnil && mdb.tag[j] != mdb2.tag[j]))
657                           break;
658                       if (j == 4
659                           && ! find_database (mdb2.tag[0], mdb2.tag[1],
660                                               mdb2.tag[2], mdb2.tag[3]))
661                         {
662                           mdb2.loader = load_database;
663                           mdb2.extra_info = calloc (1, sizeof (MDatabaseInfo));
664                           if (! mdb2.extra_info)
665                             MEMORY_FULL (MERROR_DB);
666                           ((MDatabaseInfo*) mdb2.extra_info)->filename
667                             = strdup (globbuf.gl_pathv[i]);
668                           MLIST_APPEND1 (&mdb_list, mdbs, mdb2, MERROR_DB);
669                         }
670                     }
671                   M17N_OBJECT_UNREF (p1);
672                 }
673               M17N_OBJECT_UNREF (load_key);
674               globfree (&globbuf);
675             }
676           else
677             {
678               if (find_database (mdb.tag[0], mdb.tag[1],
679                                  mdb.tag[2], mdb.tag[3]))
680                 continue;
681               SAFE_ALLOCA (path, nbytes + 1);
682               memcpy (path, mt->data, nbytes);
683               path[nbytes] = '\0';
684               mdb.extra_info = calloc (1, sizeof (MDatabaseInfo));
685               if (! mdb.extra_info)
686                 MEMORY_FULL (MERROR_DB);
687               ((MDatabaseInfo*) mdb.extra_info)->filename = strdup (path);
688               MLIST_APPEND1 (&mdb_list, mdbs, mdb, MERROR_DB);
689             }
690         }
691       M17N_OBJECT_UNREF (pl);
692     }
693
694   SAFE_FREE (path);
695 }
696
697 \f
698 /* Internal API */
699
700 /** List of database directories.  */ 
701 MPlist *mdatabase__dir_list;
702
703 int
704 mdatabase__init ()
705 {
706   MDatabaseInfo *dir_info;
707
708   Mchar_table = msymbol ("char-table");
709   Masterisk = msymbol ("*");
710
711   mdatabase__dir_list = mplist ();
712   /** The macro M17NDIR specifies a directory where the system-wide
713     MDB_DIR file exists.  */
714   if ((dir_info = get_dir_info (M17NDIR)))
715     mplist_set (mdatabase__dir_list, Mt, dir_info);
716
717   /* The variable mdatabase_dir specifies a directory where an
718      application program specific MDB_DIR file exists.  */
719   if ((dir_info = get_dir_info (mdatabase_dir)))
720     mplist_push (mdatabase__dir_list, Mt, dir_info);
721
722   /* The environment variable M17NDIR (if non-NULL) specifies a
723      directory where a user specific MDB_DIR file exists.  */
724   if ((dir_info = get_dir_info (getenv ("M17NDIR"))))
725     mplist_push (mdatabase__dir_list, Mt, dir_info);
726
727   MLIST_INIT1 (&mdb_list, mdbs, 256);
728   update_database_list ();
729
730   mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
731                        mdatabase_find);
732   mdatabase__loader = (void *(*) (void *)) mdatabase_load;
733
734   return 0;
735 }
736
737 void
738 mdatabase__fini (void)
739 {
740   int i;
741   MPlist *plist; 
742
743   MPLIST_DO (plist, mdatabase__dir_list)
744     free (MPLIST_VAL (plist));
745   M17N_OBJECT_UNREF (mdatabase__dir_list);
746
747   for (i = 0; i < mdb_list.used; i++)
748     {
749       MDatabase *mdb = mdb_list.mdbs + i;
750
751       if (mdb->loader == load_database)
752         {
753           MDatabaseInfo *db_info = mdb->extra_info;
754
755           free (db_info->filename);
756           free (db_info);
757         }
758     }
759   MLIST_FREE1 (&mdb_list, mdbs);
760 }
761
762 MPlist *
763 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
764 {
765   int mdebug_mask = MDEBUG_DATABASE;
766   FILE *fp;
767   MPlist *plist;
768   char buf[256];
769
770   if (mdb->loader != load_database
771       || mdb->tag[0] == Mchar_table
772       || mdb->tag[0] == Mcharset)
773     MERROR (MERROR_DB, NULL);
774   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
775                  gen_database_name (buf, mdb->tag));
776   fp = get_database_stream ((MDatabaseInfo *) mdb->extra_info);
777   if (! fp)
778     MERROR (MERROR_DB, NULL);
779   plist = mplist__from_file (fp, keys);
780   fclose (fp);
781   return plist;
782 }
783
784
785 /* Check if the database MDB should be reloaded or not.  */
786
787 int
788 mdatabase__check (MDatabase *mdb)
789 {
790   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
791   struct stat buf;
792
793   if (stat (db_info->filename, &buf) < 0)
794     return -1;
795   return (db_info->time >= buf.st_ctime
796           && db_info->time >= buf.st_mtime);
797 }
798
799 /*** @} */
800 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
801
802 \f
803 /* External API */
804
805 /*** @addtogroup m17nDatabase */
806 /*** @{ */
807
808 /*=*/
809 /***en
810     @brief Directory for application specific data.
811
812     If an application program wants to provide a data specific to the
813     program or a data overriding what supplied by the m17n database,
814     it must set this variable to a name of directory that contains the
815     data files before it calls the macro M17N_INIT ().  The directory
816     may contain a file "mdb.dir" which contains a list of data
817     definitions in the format described in @ref mdbDir "mdbDir(5)".
818
819     The default value is NULL.  */
820 /***ja
821     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
822
823     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n 
824     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
825     ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
826     "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ 
827     @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
828
829     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
830
831 char *mdatabase_dir;
832
833 /*=*/
834 /***en
835     @brief Look for a data in the database.
836
837     The mdatabase_find () function searches the m17n database for a
838     data who has tags $TAG0 through $TAG3, and returns a pointer to
839     the data.  If such a data is not found, it returns @c NULL.  */
840
841 /***ja
842     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
843
844     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
845     $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
846     @c NULL ¤òÊÖ¤¹¡£
847
848     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
849
850 MDatabase *
851 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
852 {
853   update_database_list ();
854   return find_database (tag0, tag1, tag2, tag3);
855 }
856
857 /*=*/
858 /***en
859     @brief Return a data list of the m17n database.
860
861     The mdatabase_list () function searches the m17n database for data
862     who have tags $TAG0 through $TAG3, and returns their list by a
863     plist.  The value #Mnil in $TAGn means a wild card that matches
864     any tag.  Each element of the plist has key #Mt and value a
865     pointer to type #MDatabase.  */
866 /***ja
867     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
868
869     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
870     ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
871     ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
872     plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
873
874 MPlist *
875 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
876 {
877   int i;
878   MPlist *plist = NULL, *pl;
879
880   update_database_list ();
881
882   for (i = 0; i < mdb_list.used; i++)
883     {
884       MDatabase *mdb = mdb_list.mdbs + i;
885
886       if ((tag0 == Mnil || tag0 == mdb->tag[0])
887           && (tag1 == Mnil || tag1 == mdb->tag[1])
888           && (tag2 == Mnil || tag2 == mdb->tag[2])
889           && (tag3 == Mnil || tag3 == mdb->tag[3]))
890         {
891           if (! plist)
892             plist = pl = mplist ();
893           pl = mplist_add (pl, Mt, mdb);
894         }
895     }
896   return plist;
897 }
898
899 /*=*/
900 /***en
901     @brief Define a data of the m17n database.
902
903     The mdatabase_define () function defines a data that has tags
904     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
905
906     $LOADER is a pointer to a function that loads the data from the
907     database.  This function is called from the mdatabase_load ()
908     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
909     $TAGS is the array of $TAG0 through $TAG3.
910
911     If $LOADER is @c NULL, the default loader of the m17n library is
912     used.  In this case, $EXTRA_INFO must be a string specifying a
913     filename that contains the data.
914
915     @return
916     If the operation was successful, mdatabase_define () returns a
917     pointer to the defined data, which can be used as an argument to
918     mdatabase_load ().  Otherwise, it returns @c NULL.  */
919
920 /***ja
921     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
922
923     ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó 
924     $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
925
926     $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
927     mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç 
928     $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
929
930     ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
931     $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
932
933     @return
934     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
935     ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
936     ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
937
938     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
939
940 /***
941     @seealso
942     mdatabase_load (),  mdatabase_define ()  */
943
944 MDatabase *
945 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
946                   void *(*loader) (MSymbol *, void *),
947                   void *extra_info)
948 {
949   MDatabase *mdb;
950
951   mdb = mdatabase_find (tag0, tag1, tag2, tag3);
952   if (! mdb)
953     {
954       MDatabase template;
955
956       template.tag[0] = tag0, template.tag[1] = tag1;
957       template.tag[2] = tag2, template.tag[3] = tag3;
958       template.extra_info = NULL;
959       MLIST_APPEND1 (&mdb_list, mdbs, template, MERROR_DB);
960       mdb = mdb_list.mdbs + (mdb_list.used - 1);
961     }
962   mdb->loader = loader ? loader : load_database;
963   if (mdb->loader == load_database)
964     {
965       MDatabaseInfo *db_info = mdb->extra_info;
966
967       if (db_info)
968         free (db_info->filename);
969       else
970         db_info = mdb->extra_info = malloc (sizeof (MDatabaseInfo));
971       db_info->filename = strdup ((char *) extra_info);
972       db_info->time = 0;
973     }
974   else
975     mdb->extra_info = extra_info;
976   return (&(mdb_list.mdbs[mdb_list.used - 1]));
977 }
978
979 /*=*/
980 /***en
981     @brief Load a data from the database.
982
983     The mdatabase_load () function loads a data specified in $MDB and
984     returns the contents.  The type of contents depends on the type of
985     the data.
986
987     If the data is of the @e plist @e type, this function returns a
988     pointer to @e plist.
989
990     If the database is of the @e chartable @e type, it returns a
991     chartable.  The default value of the chartable is set according to
992     the second tag of the data as below:
993
994     @li If the tag is #Msymbol, the default value is #Mnil.
995     @li If the tag is #Minteger, the default value is -1.
996     @li Otherwise, the default value is @c NULL.
997
998     If the data is of the @e charset @e type, it returns a plist of length 2
999     (keys are both #Mt).  The value of the first element is an array
1000     of integers that maps code points to the corresponding character
1001     codes.  The value of the second element is a chartable of integers
1002     that does the reverse mapping.  The charset must be defined in
1003     advance.  */
1004
1005
1006 /***ja
1007     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
1008
1009     ´Ø¿ô mdatabase_load () ¤Ï $MDB 
1010     ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
1011
1012     ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1013
1014     ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
1015     Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
1016
1017     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
1018     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
1019     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
1020
1021     ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
1022     ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1023     £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
1024     ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
1025
1026     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1027   */
1028
1029 /***
1030     @seealso
1031     mdatabase_load (),  mdatabase_define ()  */
1032
1033 void *
1034 mdatabase_load (MDatabase *mdb)
1035 {
1036   int mdebug_mask = MDEBUG_DATABASE;
1037   char buf[256];
1038
1039   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
1040                  gen_database_name (buf, mdb->tag));
1041   return (*mdb->loader) (mdb->tag, mdb->extra_info);
1042 }
1043
1044 /*=*/
1045 /***en
1046     @brief Get tags of a data.
1047
1048     The mdatabase_tag () function returns an array of tags (symbols)
1049     that identify the data in $MDB.  The length of the array is
1050     four.  */
1051
1052 /***ja
1053     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
1054
1055     ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
1056     4 ¤Ç¤¢¤ë¡£
1057
1058     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
1059
1060 MSymbol *
1061 mdatabase_tag (MDatabase *mdb)
1062 {
1063   return mdb->tag;
1064 }
1065
1066 /*** @} */
1067
1068 /*
1069   Local Variables:
1070   coding: euc-japan
1071   End:
1072 */