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