(mplist_conv, mplist__pop_unref): Extern them.
[m17n/m17n-lib.git] / src / database.c
1 /* database.c -- database module.
2    Copyright (C) 2003, 2004
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 /***en
24     @addtogroup m17nDatabase
25     @brief The m17n database and API for it.
26
27     The m17n library dynamically acquires various kinds of information
28     in need from data in the <i> m17n database</i>.  Application
29     programs can also add/load their original data to/from the m17n
30     database.  The m17n database contains multiple heterogeneous data,
31     and each data is identified by four tags; TAG0, TAG1, TAG2, TAG3.
32     Each tag must be a symbol.
33
34     TAG0 specifies the type of data stored in the database as below.
35
36     @li
37     If TAG0 is #Mchar_table, the data is of the @e chartable @e
38     type and provides information about each character.  In this case,
39     TAG1 specifies the type of the information and must be #Msymbol,
40     #Minteger, #Mstring, #Mtext, or #Mplist.  TAG2 and TAG3 can be any
41     symbols.
42
43     @li
44     If TAG0 is #Mcharset, the data is of the @e charset @e type
45     and provides a decode/encode mapping table for a charset.  In this
46     case, TAG1 must be a symbol representing a charset.  TAG2 and TAG3
47     can be any symbols.
48
49     @li 
50     If TAG0 is neither #Mchar_table nor #Mcharset, the data is of
51     the @e plist @e type.  See the documentation of the 
52     mdatabase_load () function for the details.  
53     In this case, TAG1, TAG2, and TAG3 can be any symbols.
54
55     The notation \<TAG0, TAG1, TAG2, TAG3\> means a data with those
56     tags.
57
58     Application programs first calls the mdatabase_find () function to
59     get a pointer to an object of the type #MDatabase.  That object
60     holds information about the specified data.  When it is
61     successfully returned, the mdatabase_load () function loads the
62     data.  The implementation of the structure #MDatabase is
63     concealed from application programs.
64 */
65
66 /***ja
67     @addtogroup m17nDatabase
68     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤È¤½¤ì¤Ë´Ø¤¹¤ë API.
69
70     m17n ¥é¥¤¥Ö¥é¥ê¤ÏɬÍפ˱þ¤¸¤ÆưŪ¤Ë @e m17n @e ¥Ç¡¼¥¿¥Ù¡¼¥¹ 
71     ¤«¤é¾ðÊó¤ò¼èÆÀ¤¹¤ë¡£¤Þ¤¿¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤âÆȼ«¤Î¥Ç¡¼¥¿¤ò 
72     m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÄɲä·¡¢¤½¤ì¤òưŪ¤Ë¼èÆÀ¤¹¤ë¤³¤È¤¬¤Ç¤­¤ë¡£m17n 
73     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë¤ÏÊ£¿ô¤Î¿Íͤʥǡ¼¥¿¤¬´Þ¤Þ¤ì¤Æ¤ª¤ê¡¢³Æ¥Ç¡¼¥¿¤Ï
74     TAG0, TAG1, TAG2, TAG3¡Ê¤¹¤Ù¤Æ¥·¥ó¥Ü¥ë¡Ë¤Î£´¤Ä¤Î¥¿¥°¤Ë¤è¤Ã¤Æ¼±Ê̤µ¤ì¤ë¡£
75
76     TAG0 ¤Ë¤è¤Ã¤Æ¡¢¥Ç¡¼¥¿¥Ù¡¼¥¹Æâ¤Î¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ï¼¡¤Î¤è¤¦¤Ë»ØÄꤵ¤ì¤ë¡£
77
78     @li 
79     TAG0 ¤¬ #Mchar_table ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e chartable¥¿¥¤¥× 
80     ¤È¸Æ¤Ð¤ì¡¢³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç
81     TAG1 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Msymbol, #Minteger, #Mstring,
82     #Mtext, #Mplist ¤Î¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£TAG2 ¤È TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
83
84     @li 
85     TAG0 ¤¬ #Mcharset ¤Ç¤¢¤ë¥Ç¡¼¥¿¤Ï @e charset¥¿¥¤¥× 
86     ¤È¸Æ¤Ð¤ì¡¢Ê¸»ú¥»¥Ã¥ÈÍѤΥǥ³¡¼¥É¡¿¥¨¥ó¥³¡¼¥É¥Þ¥Ã¥×¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1
87     ¤Ïʸ»ú¥»¥Ã¥È¤Î¥·¥ó¥Ü¥ë¤Ç¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£TAG2 ¤È TAG3
88     ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
89
90     @li
91     TAG0 ¤¬ #Mchar_table ¤Ç¤â #Mcharset ¤Ç¤â¤Ê¤¤¾ì¹ç¡¢¤½¤Î¥Ç¡¼¥¿¤Ï @e
92     plist¥¿¥¤¥× ¤Ç¤¢¤ë¡£¾ÜºÙ¤Ë´Ø¤·¤Æ¤Ï´Ø¿ô mdatabase_load () 
93     ¤ÎÀâÌÀ¤ò»²¾È¤Î¤³¤È¡£¤³¤Î¾ì¹ç TAG1¡¢TAG2¡¢TAG3 ¤ÏǤ°Õ¤Î¥·¥ó¥Ü¥ë¤Ç¤è¤¤¡£
94
95     ÆÃÄê¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò \<TAG0, TAG1, TAG2, TAG3\> 
96     ¤È¤¤¤¦·Á¼°¤Çɽ¤¹¡£
97
98     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤Ï¡¢¤Þ¤º´Ø¿ô mdatabase_find () 
99     ¤ò»È¤Ã¤Æ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ë´Ø¤¹¤ë¾ðÊó¤òÊÝ»ý¤¹¤ë¥ª¥Ö¥¸¥§¥¯¥È¡Ê#MDatabase
100     ·¿¡Ë¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÆÀ¤ë¡£¤½¤ì¤ËÀ®¸ù¤·¤¿¤é¡¢ mdatabase_load () 
101     ¤Ë¤è¤Ã¤Æ¼ÂºÝ¤Ë¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¥í¡¼¥É¤¹¤ë¡£¹½Â¤ÂΠ#MDatabase 
102     ¼«¿È¤¬¤É¤¦¼ÂÁõ¤µ¤ì¤Æ¤¤¤ë¤«¤Ï¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤«¤é¤Ï¸«¤¨¤Ê¤¤¡£
103
104     @latexonly \IPAlabel{database} @endlatexonly
105 */
106
107 /*=*/
108
109 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
110 /*** @addtogroup m17nInternal
111      @{ */
112
113 #include <config.h>
114 #include <stdio.h>
115 #include <stdlib.h>
116 #include <string.h>
117 #include <ctype.h>
118 #include <sys/types.h>
119 #include <sys/stat.h>
120 #include <unistd.h>
121 #include <limits.h>
122
123 #include "m17n.h"
124 #include "m17n-misc.h"
125 #include "internal.h"
126 #include "mtext.h"
127 #include "character.h"
128 #include "charset.h"
129 #include "database.h"
130 #include "coding.h"
131 #include "plist.h"
132
133 /** The file containing a list of databases.  */
134 #define MDB_DIR "mdb.dir"
135 #define MDB_DIR_LEN 8
136
137 /** Structure for a data in the m17n database.  */
138
139 struct MDatabase
140 {
141   /** Tags to identify the data.  <tag>[0] specifies the type of
142       database.  If it is #Mchar_table, the type is @e chartable, if
143       it is #Mcharset, the type is @e charset, otherwise the type is
144       @e plist.  */
145   MSymbol tag[4];
146
147   void *(*loader) (MSymbol *tags, void *extra_info);
148
149   /** The meaning of the value is dependent on <loader>.  If <loader>
150       is load_database (), the value is a string of the file name that
151       contains the data.  */
152   void *extra_info;
153 };
154
155 /** List of all data.  */
156 struct MDatabaseList
157 {
158   int size, inc, used;
159   MDatabase *mdbs;
160 };
161
162 static struct MDatabaseList mdb_list;
163
164
165 static int
166 read_number (char *buf, int *i)
167 {
168   int idx = *i;
169   int c = buf[idx++];
170   int n;
171
172   if (!c)
173     return -1;
174
175   while (c && isspace (c)) c = buf[idx++];
176
177   if (c == '0')
178     {
179       if (buf[idx] == 'x')
180         {
181           for (idx++, c = 0; (n = hex_mnemonic[(unsigned) buf[idx]]) < 16;
182                idx++)
183             c  = (c << 4) | n;
184           *i = idx;
185           return c;
186         }
187       c = 0;
188     }
189   else if (c == '\'')
190     {
191       c = buf[idx++];
192       if (c == '\\')
193         {
194           c = buf[idx++];
195           n = escape_mnemonic[c];
196           if (n != 255)
197             c = n;
198         }
199       while (buf[idx] && buf[idx++] != '\'');
200       *i = idx;
201       return c;
202     }
203   else if (hex_mnemonic[c] < 10)
204     c -= '0';
205   else
206     return -1;
207
208   while ((n = hex_mnemonic[(unsigned) buf[idx]]) < 10)
209     c = (c * 10) + n, idx++;
210   *i = idx;
211   return c;
212 }
213
214
215 /** Load a data of type @c chartable from the file FD, and return the
216     newly created chartable.  */
217
218 static void *
219 load_chartable (FILE *fp, MSymbol type)
220 {
221   int c, from, to;
222   char buf[1024];
223   void *val;
224   MCharTable *table;
225
226   if (! fp)
227     MERROR (MERROR_DB, NULL);
228
229   table = mchartable (type, (type == Msymbol ? (void *) Mnil
230                              : type == Minteger ? (void *) -1
231                              : NULL));
232
233   while (! feof (fp))
234     {
235       int i, len;
236
237       for (len = 0; len < 1023 && (c = getc (fp)) != EOF && c != '\n'; len++)
238         buf[len] = c;
239       buf[len] = '\0';    
240       if (hex_mnemonic[(unsigned) buf[0]] >= 10)
241         /* skip comment/invalid line */
242         continue;
243       i = 0;
244       from = read_number (buf, &i);
245       if (buf[i] == '-')
246         i++, to = read_number (buf, &i);
247       else
248         to = from;
249       if (from < 0 || to < from)
250         continue;
251
252       while (buf[i] && isspace ((unsigned) buf[i])) i++;
253       c = buf[i];
254       if (!c)
255         continue;
256
257       if (type == Mstring)
258         {
259           /* VAL is a C-string.  */
260           if (! (val = strdup (buf + i)))
261             MEMORY_FULL (MERROR_DB);
262         }
263       else if (type == Minteger)
264         {
265           /* VAL is an integer.  */
266           int positive = 1;
267           int n;
268
269           if (c == '-')
270             i++, positive = -1;
271           n = read_number (buf, &i);
272           if (n < 0)
273             goto label_error;
274           val = (void *) (n * positive);
275         }
276       else if (type == Mtext)
277         {
278           /* VAL is an M-text.  */
279           MText *mt;
280           if (c == '"')
281             mt = mconv_decode_buffer (Mcoding_utf_8,
282                                       (unsigned char *) (buf + i),
283                                       len - i - 1);
284           else
285             {
286               mt = mtext ();
287               while ((c = read_number (buf, &i)) >= 0)
288                 mt = mtext_cat_char (mt, c);
289             }
290           val = (void *) mt;
291         }
292       else if (type == Msymbol)
293         {
294           char *p = buf + i;
295
296           while (*p && ! isspace (*p)) 
297             {
298               if (*p == '\\' && p[1] != '\0')
299                 {
300                   memmove (p, p + 1, buf + len - (p + 1));
301                   len--;
302                 }
303               p++;
304             }
305           *p = '\0';
306           if (! strcmp (buf + i, "nil"))
307             val = (void *) Mnil;
308           else
309             val = (void *) msymbol (buf + i);
310         }
311       else if (type == Mplist)
312         {
313           val = (void *) mplist__from_string ((unsigned char *) buf + i,
314                                               strlen (buf + i));
315         }
316       else
317         val = NULL;
318
319       if (from == to)
320         mchartable_set (table, from, val);
321       else
322         mchartable_set_range (table, from, to, val);
323     }
324   return table;
325
326  label_error:
327   M17N_OBJECT_UNREF (table);
328   MERROR (MERROR_DB, NULL);
329 }
330
331
332 /** Load a data of type @c charset from the file FD.  */
333
334 static void *
335 load_charset (FILE *fp, MSymbol charset_name)
336 {
337   MCharset *charset = MCHARSET (charset_name);
338   int *decoder;
339   MCharTable *encoder;
340   int size;
341   int i, c;
342   int found = 0;
343   MPlist *plist;
344
345   if (! charset)
346     MERROR (MERROR_DB, NULL);
347   size = (charset->code_range[15]
348           - (charset->min_code - charset->code_range_min_code));
349   MTABLE_MALLOC (decoder, size, MERROR_DB);
350   for (i = 0; i < size; i++)
351     decoder[i] = -1;
352   encoder = mchartable (Minteger, (void *) MCHAR_INVALID_CODE);
353
354   while ((c = getc (fp)) != EOF)
355     {
356       unsigned code1, code2, c1, c2;
357       int idx1, idx2;
358       char buf[256];
359
360       ungetc (c, fp);
361       fgets (buf, 256, fp);
362       if (c != '#')
363         {
364           if (sscanf (buf, "0x%x-0x%x 0x%x", &code1, &code2, &c1) == 3)
365             {
366               idx1 = CODE_POINT_TO_INDEX (charset, code1);
367               if (idx1 >= size)
368                 continue;
369               idx2 = CODE_POINT_TO_INDEX (charset, code2);
370               if (idx2 >= size)
371                 idx2 = size - 1;
372               c2 = c1 + (idx2 - idx1);
373             }
374           else if (sscanf (buf, "0x%x 0x%x", &code1, &c1) == 2)
375             {
376               idx1 = idx2 = CODE_POINT_TO_INDEX (charset, code1);
377               if (idx1 >= size)
378                 continue;
379               c2 = c1;
380             }
381           else
382             continue;
383           if (idx1 >= 0 && idx2 >= 0)
384             {
385               decoder[idx1] = c1;
386               mchartable_set (encoder, c1, (void *) code1);
387               for (idx1++, c1++; idx1 <= idx2; idx1++, c1++)
388                 {
389                   code1 = INDEX_TO_CODE_POINT (charset, idx1);
390                   decoder[idx1] = c1;
391                   mchartable_set (encoder, c1, (void *) code1);
392                 }
393               found++;
394             }
395         }
396     }
397
398   if (! found)
399     {
400       free (decoder);
401       M17N_OBJECT_UNREF (encoder);
402       return NULL;
403     }
404   plist = mplist ();
405   mplist_add (plist, Mt, decoder);
406   mplist_add (plist, Mt, encoder);
407   return plist;
408 }
409
410 static char *
411 gen_database_name (char *buf, MSymbol *tags)
412 {
413   int i;
414
415   strcpy (buf, msymbol_name (tags[0]));
416   for (i = 1; i < 4; i++)
417     {
418       strcat (buf, ", ");
419       strcat (buf, msymbol_name (tags[i]));
420     }
421   return buf;
422 }
423
424 static FILE *
425 get_database_stream (char *filename)
426 {
427   FILE *fp = NULL;
428
429   if (filename[0] == '/')
430     fp = fopen (filename, "r");
431   else
432     {
433       MPlist *plist;
434       char *path;
435       int filelen = strlen (filename);
436       USE_SAFE_ALLOCA;
437
438       MPLIST_DO (plist, mdatabase__dir_list)
439         {
440           int require = strlen ((char *) MPLIST_VAL (plist)) + filelen + 1;
441
442           SAFE_ALLOCA (path, require);
443           strcpy (path, (char *) MPLIST_VAL (plist));
444           strcat (path, filename);
445           fp = fopen (path, "r");
446           if (fp)
447             break;
448         }
449       SAFE_FREE (path);
450     }
451   return fp;
452 }
453
454 static void *
455 load_database (MSymbol *tags, void *extra_info)
456 {
457   FILE *fp = get_database_stream ((char *) extra_info);
458   void *value;
459
460   if (! fp)
461     MERROR (MERROR_DB, NULL);
462
463   if (tags[0] == Mchar_table)
464     value = load_chartable (fp, tags[1]);
465   else if (tags[0] == Mcharset)
466     value = load_charset (fp, tags[1]);
467   else
468     value = mplist__from_file (fp, NULL);
469   fclose (fp);
470
471   if (! value)
472     MERROR (MERROR_DB, NULL);
473   return value;
474 }
475
476
477 /** Copy DIRNAME to a newly allocated memory and return it.  If
478     DIRNAME does not end with a slash, append a slash to the new memory.  */
479
480 static char *
481 duplicate_dirname (char *dirname)
482 {
483   struct stat buf;
484   int len;
485   char *str;
486
487   if (! dirname
488       || stat (dirname, &buf) < 0)
489     return NULL;
490
491   len = strlen (dirname);
492   MTABLE_MALLOC (str, len + 2, MERROR_DB);
493   memcpy (str, dirname, len + 1);
494   if (str[len - 1] != '/')
495     {
496       str[len] = '/';
497       str[len + 1] = '\0';
498     }
499   return str;
500 }
501
502 \f
503 /* Internal API */
504
505 /** List of database directories.  */ 
506 MPlist *mdatabase__dir_list;
507
508 MSymbol M_database_hook;
509
510 int
511 mdatabase__init ()
512 {
513   char *dir;
514   int i;
515   MPlist *plist;
516   FILE *fp;
517   char *path;
518   USE_SAFE_ALLOCA;
519
520   Mchar_table = msymbol ("char-table");
521   M_database_hook = msymbol ("  database-hook");
522
523   mdatabase__dir_list = mplist ();
524   /** The macro M17NDIR specifies a directory where the system-wide
525     MDB_DIR file exists.  */
526   if ((dir = duplicate_dirname (M17NDIR)))
527     mplist_set (mdatabase__dir_list, Mt, dir);
528
529   /* The variable mdatabase_dir specifies a directory where an
530      application program specific MDB_DIR file exists.  */
531   if ((dir = duplicate_dirname (mdatabase_dir)))
532     mplist_push (mdatabase__dir_list, Mt, dir);
533
534   /* The environment variable M17NDIR (if non-NULL) specifies a
535      directory where a user specific MDB_DIR file exists.  */
536   if ((dir = duplicate_dirname (getenv ("M17NDIR"))))
537     mplist_push (mdatabase__dir_list, Mt, dir);
538
539   MLIST_INIT1 (&mdb_list, mdbs, 256);
540   MPLIST_DO (plist, mdatabase__dir_list)
541     {
542       MPlist *pl, *p;
543       int len;
544
545       dir = (char *) MPLIST_VAL (plist);
546       len = strlen (dir);
547 #ifdef PATH_MAX
548       if (len + MDB_DIR_LEN >= PATH_MAX)
549         continue;
550 #endif  /* PATH_MAX */
551       SAFE_ALLOCA (path, len + MDB_DIR_LEN);
552       memcpy (path, dir, len);
553       memcpy (path + len, MDB_DIR, MDB_DIR_LEN);
554       if (! (fp = fopen (path, "r")))
555         continue;
556       pl = mplist__from_file (fp, NULL);
557       fclose (fp);
558       if (! pl)
559         continue;
560       MPLIST_DO (p, pl)
561         {
562           MDatabase mdb;
563           MPlist *p1;
564           MText *mt;
565           int nbytes;
566
567           if (! MPLIST_PLIST_P (p))
568             continue;
569           for (i = 0, p1 = MPLIST_PLIST (p);
570                i < 4 && MPLIST_KEY (p1) == Msymbol;
571                i++, p1 = MPLIST_NEXT (p1))
572             mdb.tag[i] = MPLIST_SYMBOL (p1);
573           if (i == 0
574               || ! MPLIST_MTEXT_P (p1))
575             continue;
576           for (; i < 4; i++)
577             mdb.tag[i] = Mnil;
578           if (mdatabase_find (mdb.tag[0], mdb.tag[1],
579                               mdb.tag[2], mdb.tag[3]))
580             continue;
581
582           mdb.loader = load_database;
583           mt = MPLIST_MTEXT (p1);
584           if (mt->format >= MTEXT_FORMAT_UTF_16LE)
585             mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
586           nbytes = mtext_nbytes (mt);
587 #ifdef PATH_MAX
588           if (nbytes > PATH_MAX)
589             continue;
590 #endif  /* PATH_MAX */
591           SAFE_ALLOCA (path, nbytes + 1);
592           memcpy (path, mt->data, nbytes);
593           path[nbytes] = '\0';
594           mdb.extra_info = (void *) strdup (path);
595           if (! mdb.extra_info)
596             MEMORY_FULL (MERROR_DB);
597           MLIST_APPEND1 (&mdb_list, mdbs, mdb, MERROR_DB);
598         }
599       M17N_OBJECT_UNREF (pl);
600     }
601
602   SAFE_FREE (path);
603
604   mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
605                        mdatabase_find);
606   mdatabase__loader = (void *(*) (void *)) mdatabase_load;
607
608   return 0;
609 }
610
611 void
612 mdatabase__fini (void)
613 {
614   int i;
615   MPlist *plist; 
616
617   MPLIST_DO (plist, mdatabase__dir_list)
618     free (MPLIST_VAL (plist));
619   M17N_OBJECT_UNREF (mdatabase__dir_list);
620
621   for (i = 0; i < mdb_list.used; i++)
622     {
623       MDatabase *mdb = mdb_list.mdbs + i;
624
625       if (mdb->loader == load_database)
626         free (mdb->extra_info);
627     }
628   MLIST_FREE1 (&mdb_list, mdbs);
629 }
630
631 MPlist *
632 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
633 {
634   int mdebug_mask = MDEBUG_DATABASE;
635   FILE *fp;
636   MPlist *plist;
637   char buf[256];
638
639   if (mdb->loader != load_database
640       || mdb->tag[0] == Mchar_table
641       || mdb->tag[0] == Mcharset)
642     MERROR (MERROR_DB, NULL);
643   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
644                  gen_database_name (buf, mdb->tag));
645   fp = get_database_stream ((char *) mdb->extra_info);
646   if (! fp)
647     MERROR (MERROR_DB, NULL);
648   plist = mplist__from_file (fp, keys);
649   fclose (fp);
650   return plist;
651 }
652
653
654 /*** @} */
655 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
656
657 \f
658 /* External API */
659
660 /*** @addtogroup m17nDatabase */
661 /*** @{ */
662
663 /*=*/
664 /***en
665     @brief Directory for application specific data.
666
667     If an application program wants to provide a data specific to the
668     program or a data overriding what supplied by the m17n database,
669     it must set this variable to a name of directory that contains the
670     data files before it calls the macro M17N_INIT ().  The directory
671     may contain a file "mdb.dir" which contains a list of data
672     definitions in the format described in @ref mdbDir "mdbDir(5)".
673
674     The default value is NULL.  */
675 /***ja
676     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
677
678     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n 
679     ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
680     ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï
681     "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ 
682     @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
683
684     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
685
686 char *mdatabase_dir;
687
688 /*=*/
689 /***en
690     @brief Look for a data in the database.
691
692     The mdatabase_find () function searches the m17n database for a
693     data who has tags $TAG0 through $TAG3, and returns a pointer to
694     the data.  If such a data is not found, it returns @c NULL.  */
695
696 /***ja
697     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
698
699     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
700     $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð
701     @c NULL ¤òÊÖ¤¹¡£
702
703     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
704
705 MDatabase *
706 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
707 {
708   int i;
709   MDatabaseHookFunc func
710     = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook);
711
712   if (func)
713     func (tag0, tag1, tag2, tag3);
714
715   for (i = 0; i < mdb_list.used; i++)
716     {
717       MDatabase *mdb = mdb_list.mdbs + i;
718
719       if (tag0 == mdb->tag[0]
720           && tag1 == mdb->tag[1]
721           && tag2 == mdb->tag[2]
722           && tag3 == mdb->tag[3])
723         return mdb;
724     }
725   return NULL;
726 }
727
728 /*=*/
729 /***en
730     @brief Return a data list of the m17n database.
731
732     The mdatabase_list () function searches the m17n database for data
733     who have tags $TAG0 through $TAG3, and returns their list by a
734     plist.  The value #Mnil in $TAGn means a wild card that matches
735     any tag.  Each element of the plist has key #Mt and value a
736     pointer to type #MDatabase.  */
737 /***ja
738     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
739
740     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
741     ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn ¤¬ #Mnil
742     ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë
743     plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃͤȤ·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
744
745
746 MPlist *
747 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
748 {
749   int i;
750   MPlist *plist = NULL, *pl;
751   MDatabaseHookFunc func
752     = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook);
753
754   if (func)
755     func (tag0, tag1, tag2, tag3);
756
757   for (i = 0; i < mdb_list.used; i++)
758     {
759       MDatabase *mdb = mdb_list.mdbs + i;
760
761       if ((tag0 == Mnil || tag0 == mdb->tag[0])
762           && (tag1 == Mnil || tag1 == mdb->tag[1])
763           && (tag2 == Mnil || tag2 == mdb->tag[2])
764           && (tag3 == Mnil || tag3 == mdb->tag[3]))
765         {
766           if (! plist)
767             plist = pl = mplist ();
768           pl = mplist_add (pl, Mt, mdb);
769         }
770     }
771   return plist;
772 }
773
774
775
776 /*=*/
777 /***en
778     @brief Define a data of the m17n database.
779
780     The mdatabase_define () function defines a data that has tags
781     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
782
783     $LOADER is a pointer to a function that loads the data from the
784     database.  This function is called from the mdatabase_load ()
785     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
786     $TAGS is the array of $TAG0 through $TAG3.
787
788     If $LOADER is @c NULL, the default loader of the m17n library is
789     used.  In this case, $EXTRA_INFO must be a string specifying a
790     filename that contains the data.
791
792     @return
793     If the operation was successful, mdatabase_define () returns a
794     pointer to the defined data, which can be used as an argument to
795     mdatabase_load ().  Otherwise, it returns @c NULL.  */
796
797 /***ja
798     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
799
800     ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉղþðÊó 
801     $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
802
803     $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³¤Î´Ø¿ô¤Ï
804     mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦Æó¤Ä¤Î°ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç 
805     $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛÎó¤Ç¤¢¤ë¡£
806
807     ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï
808     $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
809
810     @return
811     ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () 
812     ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () 
813     ¤Î°ú¿ô¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
814
815     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
816
817 /***
818     @seealso
819     mdatabase_load (),  mdatabase_define ()  */
820
821 MDatabase *
822 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
823                   void *(*loader) (MSymbol *, void *),
824                   void *extra_info)
825 {
826   MDatabase *mdb;
827   MDatabaseHookFunc func
828     = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook);
829
830   if (func)
831     func (tag0, tag1, tag2, tag3);
832
833   mdb = mdatabase_find (tag0, tag1, tag2, tag3);
834   if (! mdb)
835     {
836       MDatabase template;
837
838       template.tag[0] = tag0, template.tag[1] = tag1;
839       template.tag[2] = tag2, template.tag[3] = tag3;
840       template.extra_info = NULL;
841       MLIST_APPEND1 (&mdb_list, mdbs, template, MERROR_DB);
842       mdb = mdb_list.mdbs + (mdb_list.used - 1);
843     }
844   mdb->loader = loader ? loader : load_database;
845   if (mdb->loader == load_database)
846     {
847       if (mdb->extra_info)
848         free (mdb->extra_info);
849       mdb->extra_info = strdup ((char *) extra_info);
850     }
851   else
852     mdb->extra_info = extra_info;
853   return (&(mdb_list.mdbs[mdb_list.used - 1]));
854 }
855
856 /*=*/
857 /***en
858     @brief Load a data from the database.
859
860     The mdatabase_load () function loads a data specified in $MDB and
861     returns the contents.  The type of contents depends on the type of
862     the data.
863
864     If the data is of the @e plist @e type, this function returns a
865     pointer to @e plist.
866
867     If the database is of the @e chartable @e type, it returns a
868     chartable.  The default value of the chartable is set according to
869     the second tag of the data as below:
870
871     @li If the tag is #Msymbol, the default value is #Mnil.
872     @li If the tag is #Minteger, the default value is -1.
873     @li Otherwise, the default value is @c NULL.
874
875     If the data is of the @e charset @e type, it returns a plist of length 2
876     (keys are both #Mt).  The value of the first element is an array
877     of integers that maps code points to the corresponding character
878     codes.  The value of the second element is a chartable of integers
879     that does the reverse mapping.  The charset must be defined in
880     advance.  */
881
882
883 /***ja
884     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
885
886     ´Ø¿ô mdatabase_load () ¤Ï $MDB 
887     ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤ÎÃæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
888
889     ¥Ç¡¼¥¿¤¬ @e plist¥¿¥¤¥× ¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
890
891     ¥Ç¡¼¥¿¤¬ @e chartable¥¿¥¤¥× ¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£
892     Ê¸»ú¥Æ¡¼¥Ö¥ë¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
893
894     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
895     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
896     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
897
898     ¥Ç¡¼¥¿¤¬ @e charset¥¿¥¤¥× ¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë#Mt ¡Ë¡£
899     ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡£
900     £²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö¥ë¤Ç¤¢¤ë¡£
901     ¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
902
903     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
904   */
905
906 /***
907     @seealso
908     mdatabase_load (),  mdatabase_define ()  */
909
910 void *
911 mdatabase_load (MDatabase *mdb)
912 {
913   int mdebug_mask = MDEBUG_DATABASE;
914   char buf[256];
915
916   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
917                  gen_database_name (buf, mdb->tag));
918   return (*mdb->loader) (mdb->tag, mdb->extra_info);
919 }
920
921 /*=*/
922 /***en
923     @brief Get tags of a data.
924
925     The mdatabase_tag () function returns an array of tags (symbols)
926     that identify the data in $MDB.  The length of the array is
927     four.  */
928
929 /***ja
930     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
931
932     ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï
933     4 ¤Ç¤¢¤ë¡£
934
935     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
936
937 MSymbol *
938 mdatabase_tag (MDatabase *mdb)
939 {
940   return mdb->tag;
941 }
942
943 /*** @} */
944
945 /*
946   Local Variables:
947   coding: euc-japan
948   End:
949 */