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