(xft_render): Check xft_draw's drawable before changing
[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     {
745       MDatabaseInfo *dir_info = MPLIST_VAL (plist);
746
747       free (dir_info->filename);
748       free (dir_info);
749     }
750   M17N_OBJECT_UNREF (mdatabase__dir_list);
751
752   for (i = 0; i < mdb_list.used; i++)
753     {
754       MDatabase *mdb = mdb_list.mdbs + i;
755
756       if (mdb->loader == load_database)
757         {
758           MDatabaseInfo *db_info = mdb->extra_info;
759
760           free (db_info->filename);
761           free (db_info);
762         }
763     }
764   MLIST_FREE1 (&mdb_list, mdbs);
765 }
766
767 MPlist *
768 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
769 {
770   int mdebug_mask = MDEBUG_DATABASE;
771   FILE *fp;
772   MPlist *plist;
773   char buf[256];
774
775   if (mdb->loader != load_database
776       || mdb->tag[0] == Mchar_table
777       || mdb->tag[0] == Mcharset)
778     MERROR (MERROR_DB, NULL);
779   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
780                  gen_database_name (buf, mdb->tag));
781   fp = get_database_stream ((MDatabaseInfo *) mdb->extra_info);
782   if (! fp)
783     MERROR (MERROR_DB, NULL);
784   plist = mplist__from_file (fp, keys);
785   fclose (fp);
786   return plist;
787 }
788
789
790 /* Check if the database MDB should be reloaded or not.  */
791
792 int
793 mdatabase__check (MDatabase *mdb)
794 {
795   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
796   struct stat buf;
797
798   if (stat (db_info->filename, &buf) < 0)
799     return -1;
800   return (db_info->time >= buf.st_ctime
801           && db_info->time >= buf.st_mtime);
802 }
803
804 /*** @} */
805 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
806
807 \f
808 /* External API */
809
810 /*** @addtogroup m17nDatabase */
811 /*** @{ */
812
813 /*=*/
814 /***en
815     @brief Directory for application specific data.
816
817     If an application program wants to provide a data specific to the
818     program or a data overriding what supplied by the m17n database,
819     it must set this variable to a name of directory that contains the
820     data files before it calls the macro M17N_INIT ().  The directory
821     may contain a file "mdb.dir" which contains a list of data
822     definitions in the format described in @ref mdbDir "mdbDir(5)".
823
824     The default value is NULL.  */
825 /***ja
826     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
827
828     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n 
829     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
830     ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
831     "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ 
832     @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
833
834     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
835
836 char *mdatabase_dir;
837
838 /*=*/
839 /***en
840     @brief Look for a data in the database.
841
842     The mdatabase_find () function searches the m17n database for a
843     data who has tags $TAG0 through $TAG3, and returns a pointer to
844     the data.  If such a data is not found, it returns @c NULL.  */
845
846 /***ja
847     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
848
849     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
850     $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
851     @c NULL ¤òÊÖ¤¹¡£
852
853     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
854
855 MDatabase *
856 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
857 {
858   update_database_list ();
859   return find_database (tag0, tag1, tag2, tag3);
860 }
861
862 /*=*/
863 /***en
864     @brief Return a data list of the m17n database.
865
866     The mdatabase_list () function searches the m17n database for data
867     who have tags $TAG0 through $TAG3, and returns their list by a
868     plist.  The value #Mnil in $TAGn means a wild card that matches
869     any tag.  Each element of the plist has key #Mt and value a
870     pointer to type #MDatabase.  */
871 /***ja
872     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
873
874     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
875     ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
876     ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
877     plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
878
879 MPlist *
880 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
881 {
882   int i;
883   MPlist *plist = NULL, *pl;
884
885   update_database_list ();
886
887   for (i = 0; i < mdb_list.used; i++)
888     {
889       MDatabase *mdb = mdb_list.mdbs + i;
890
891       if ((tag0 == Mnil || tag0 == mdb->tag[0])
892           && (tag1 == Mnil || tag1 == mdb->tag[1])
893           && (tag2 == Mnil || tag2 == mdb->tag[2])
894           && (tag3 == Mnil || tag3 == mdb->tag[3]))
895         {
896           if (! plist)
897             plist = pl = mplist ();
898           pl = mplist_add (pl, Mt, mdb);
899         }
900     }
901   return plist;
902 }
903
904 /*=*/
905 /***en
906     @brief Define a data of the m17n database.
907
908     The mdatabase_define () function defines a data that has tags
909     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
910
911     $LOADER is a pointer to a function that loads the data from the
912     database.  This function is called from the mdatabase_load ()
913     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
914     $TAGS is the array of $TAG0 through $TAG3.
915
916     If $LOADER is @c NULL, the default loader of the m17n library is
917     used.  In this case, $EXTRA_INFO must be a string specifying a
918     filename that contains the data.
919
920     @return
921     If the operation was successful, mdatabase_define () returns a
922     pointer to the defined data, which can be used as an argument to
923     mdatabase_load ().  Otherwise, it returns @c NULL.  */
924
925 /***ja
926     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
927
928     ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó 
929     $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
930
931     $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
932     mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç 
933     $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
934
935     ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
936     $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
937
938     @return
939     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
940     ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
941     ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
942
943     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
944
945 /***
946     @seealso
947     mdatabase_load (),  mdatabase_define ()  */
948
949 MDatabase *
950 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
951                   void *(*loader) (MSymbol *, void *),
952                   void *extra_info)
953 {
954   MDatabase *mdb;
955
956   mdb = mdatabase_find (tag0, tag1, tag2, tag3);
957   if (! mdb)
958     {
959       MDatabase template;
960
961       template.tag[0] = tag0, template.tag[1] = tag1;
962       template.tag[2] = tag2, template.tag[3] = tag3;
963       template.extra_info = NULL;
964       MLIST_APPEND1 (&mdb_list, mdbs, template, MERROR_DB);
965       mdb = mdb_list.mdbs + (mdb_list.used - 1);
966     }
967   mdb->loader = loader ? loader : load_database;
968   if (mdb->loader == load_database)
969     {
970       MDatabaseInfo *db_info = mdb->extra_info;
971
972       if (db_info)
973         free (db_info->filename);
974       else
975         db_info = mdb->extra_info = malloc (sizeof (MDatabaseInfo));
976       db_info->filename = strdup ((char *) extra_info);
977       db_info->time = 0;
978     }
979   else
980     mdb->extra_info = extra_info;
981   return (&(mdb_list.mdbs[mdb_list.used - 1]));
982 }
983
984 /*=*/
985 /***en
986     @brief Load a data from the database.
987
988     The mdatabase_load () function loads a data specified in $MDB and
989     returns the contents.  The type of contents depends on the type of
990     the data.
991
992     If the data is of the @e plist @e type, this function returns a
993     pointer to @e plist.
994
995     If the database is of the @e chartable @e type, it returns a
996     chartable.  The default value of the chartable is set according to
997     the second tag of the data as below:
998
999     @li If the tag is #Msymbol, the default value is #Mnil.
1000     @li If the tag is #Minteger, the default value is -1.
1001     @li Otherwise, the default value is @c NULL.
1002
1003     If the data is of the @e charset @e type, it returns a plist of length 2
1004     (keys are both #Mt).  The value of the first element is an array
1005     of integers that maps code points to the corresponding character
1006     codes.  The value of the second element is a chartable of integers
1007     that does the reverse mapping.  The charset must be defined in
1008     advance.  */
1009
1010
1011 /***ja
1012     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
1013
1014     ´Ø¿ô mdatabase_load () ¤Ï $MDB 
1015     ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
1016
1017     ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
1018
1019     ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
1020     Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
1021
1022     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
1023     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
1024     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
1025
1026     ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
1027     ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
1028     £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
1029     ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
1030
1031     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1032   */
1033
1034 /***
1035     @seealso
1036     mdatabase_load (),  mdatabase_define ()  */
1037
1038 void *
1039 mdatabase_load (MDatabase *mdb)
1040 {
1041   int mdebug_mask = MDEBUG_DATABASE;
1042   char buf[256];
1043
1044   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
1045                  gen_database_name (buf, mdb->tag));
1046   return (*mdb->loader) (mdb->tag, mdb->extra_info);
1047 }
1048
1049 /*=*/
1050 /***en
1051     @brief Get tags of a data.
1052
1053     The mdatabase_tag () function returns an array of tags (symbols)
1054     that identify the data in $MDB.  The length of the array is
1055     four.  */
1056
1057 /***ja
1058     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
1059
1060     ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
1061     4 ¤Ç¤¢¤ë¡£
1062
1063     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
1064
1065 MSymbol *
1066 mdatabase_tag (MDatabase *mdb)
1067 {
1068   return mdb->tag;
1069 }
1070
1071 /*** @} */
1072
1073 /*
1074   Local Variables:
1075   coding: euc-japan
1076   End:
1077 */