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