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