(mdatabase__dir_list): Renamed from mdb_dir_list.
[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     ³Æʸ»ú¤Ë´Ø¤¹¤ë¾ðÊó¤òÄ󶡤¹¤ë¡£¤³¤Î¾ì¹ç TAG1 ¤Ï¾ðÊó¤Î¼ïÎà¤ò»ØÄꤹ¤ë
81     ¥·¥ó¥Ü¥ë¤Ç¤¢¤ê¡¢#Msymbol, #Minteger, #Mstring, #Mtext, #Mplist ¤Î
82     ¤¤¤º¤ì¤«¤Ç¤¢¤ë¡£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 < 0)
250         goto label_error;
251
252       while (buf[i] && isspace ((unsigned) buf[i])) i++;
253       c = buf[i];
254       if (!c)
255         break;
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           if (! strcmp (buf + i, "nil"))
295             val = (void *) Mnil;
296           else
297             val = (void *) msymbol (buf + i);
298         }
299       else if (type == Mplist)
300         {
301           val = (void *) mplist__from_string ((unsigned char *) buf + i,
302                                               strlen (buf + i));
303         }
304       else
305         val = NULL;
306
307       if (from == to)
308         mchartable_set (table, from, val);
309       else
310         mchartable_set_range (table, from, to, val);
311     }
312   return table;
313
314  label_error:
315   M17N_OBJECT_UNREF (table);
316   MERROR (MERROR_DB, NULL);
317 }
318
319
320 /** Load a data of type @c charset from the file FD.  */
321
322 static void *
323 load_charset (FILE *fp, MSymbol charset_name)
324 {
325   MCharset *charset = MCHARSET (charset_name);
326   int *decoder;
327   MCharTable *encoder;
328   int size;
329   int i, c;
330   int found = 0;
331   MPlist *plist;
332
333   if (! charset)
334     MERROR (MERROR_DB, NULL);
335   size = (charset->code_range[15]
336           - (charset->min_code - charset->code_range_min_code));
337   MTABLE_MALLOC (decoder, size, MERROR_DB);
338   for (i = 0; i < size; i++)
339     decoder[i] = -1;
340   encoder = mchartable (Minteger, (void *) MCHAR_INVALID_CODE);
341
342   while ((c = getc (fp)) != EOF)
343     {
344       unsigned code1, code2, c1, c2;
345       int idx1, idx2;
346       char buf[256];
347
348       ungetc (c, fp);
349       fgets (buf, 256, fp);
350       if (c != '#')
351         {
352           if (sscanf (buf, "0x%x-0x%x 0x%x", &code1, &code2, &c1) == 3)
353             {
354               idx1 = CODE_POINT_TO_INDEX (charset, code1);
355               if (idx1 >= size)
356                 continue;
357               idx2 = CODE_POINT_TO_INDEX (charset, code2);
358               if (idx2 >= size)
359                 idx2 = size - 1;
360               c2 = c1 + (idx2 - idx1);
361             }
362           else if (sscanf (buf, "0x%x 0x%x", &code1, &c1) == 2)
363             {
364               idx1 = idx2 = CODE_POINT_TO_INDEX (charset, code1);
365               if (idx1 >= size)
366                 continue;
367               c2 = c1;
368             }
369           else
370             continue;
371           if (idx1 >= 0 && idx2 >= 0)
372             {
373               decoder[idx1] = c1;
374               mchartable_set (encoder, c1, (void *) code1);
375               for (idx1++, c1++; idx1 <= idx2; idx1++, c1++)
376                 {
377                   code1 = INDEX_TO_CODE_POINT (charset, idx1);
378                   decoder[idx1] = c1;
379                   mchartable_set (encoder, c1, (void *) code1);
380                 }
381               found++;
382             }
383         }
384     }
385
386   if (! found)
387     {
388       free (decoder);
389       M17N_OBJECT_UNREF (encoder);
390       return NULL;
391     }
392   plist = mplist ();
393   mplist_add (plist, Mt, decoder);
394   mplist_add (plist, Mt, encoder);
395   return plist;
396 }
397
398 static char *
399 gen_database_name (char *buf, MSymbol *tags)
400 {
401   int i;
402
403   strcpy (buf, msymbol_name (tags[0]));
404   for (i = 1; i < 4; i++)
405     {
406       strcat (buf, ", ");
407       strcat (buf, msymbol_name (tags[i]));
408     }
409   return buf;
410 }
411
412 static FILE *
413 get_database_stream (char *filename)
414 {
415   FILE *fp = NULL;
416
417   if (filename[0] == '/')
418     fp = fopen (filename, "r");
419   else
420     {
421       MPlist *plist;
422       char path[PATH_MAX];
423
424       MPLIST_DO (plist, mdatabase__dir_list)
425         {
426           strcpy (path, (char *) MPLIST_VAL (plist));
427           strcat (path, filename);
428           fp = fopen (path, "r");
429           if (fp)
430             break;
431         }
432     }
433   return fp;
434 }
435
436 static void *
437 load_database (MSymbol *tags, void *extra_info)
438 {
439   FILE *fp = get_database_stream ((char *) extra_info);
440   void *value;
441
442   if (! fp)
443     MERROR (MERROR_DB, NULL);
444
445   if (tags[0] == Mchar_table)
446     value = load_chartable (fp, tags[1]);
447   else if (tags[0] == Mcharset)
448     value = load_charset (fp, tags[1]);
449   else
450     value = mplist__from_file (fp, NULL);
451   fclose (fp);
452
453   if (! value)
454     MERROR (MERROR_DB, NULL);
455   return value;
456 }
457
458
459 /** Copy DIRNAME to a newly allocated memory and return it.  If
460     DIRNAME does not end with a slash, append a slash to the new memory.  */
461
462 static char *
463 duplicate_dirname (char *dirname)
464 {
465   struct stat buf;
466   int len;
467   char *str;
468
469   if (! dirname
470       || stat (dirname, &buf) < 0)
471     return NULL;
472
473   len = strlen (dirname);
474   MTABLE_MALLOC (str, len + 2, MERROR_DB);
475   memcpy (str, dirname, len + 1);
476   if (str[len - 1] != '/')
477     {
478       str[len] = '/';
479       str[len + 1] = '\0';
480     }
481   return str;
482 }
483
484 \f
485 /* Internal API */
486
487 /** List of database directories.  */ 
488 MPlist *mdatabase__dir_list;
489
490 MSymbol M_database_hook;
491
492 int
493 mdatabase__init ()
494 {
495   char *dir;
496   int i;
497   MPlist *plist;
498   FILE *fp;
499
500   Mchar_table = msymbol ("char-table");
501   M_database_hook = msymbol ("  database-hook");
502
503   mdatabase__dir_list = mplist ();
504   /** The macro M17NDIR specifies a directory where the system-wide
505     MDB_DIR file exists.  */
506   if ((dir = duplicate_dirname (M17NDIR)))
507     mplist_set (mdatabase__dir_list, Mt, dir);
508
509   /* The variable mdatabase_dir specifies a directory where an
510      application program specific MDB_DIR file exists.  */
511   if ((dir = duplicate_dirname (mdatabase_dir)))
512     mplist_push (mdatabase__dir_list, Mt, dir);
513
514   /* The environment variable M17NDIR (if non-NULL) specifies a
515      directory where a user specific MDB_DIR file exists.  */
516   if ((dir = duplicate_dirname (getenv ("M17NDIR"))))
517     mplist_push (mdatabase__dir_list, Mt, dir);
518
519   MLIST_INIT1 (&mdb_list, mdbs, 256);
520   MPLIST_DO (plist, mdatabase__dir_list)
521     {
522       MPlist *pl, *p;
523       int len;
524       char path[PATH_MAX];
525
526       dir = (char *) MPLIST_VAL (plist);
527       len = strlen (dir);
528       if (len + MDB_DIR_LEN >= PATH_MAX)
529         continue;
530       memcpy (path, dir, len);
531       memcpy (path + len, MDB_DIR, MDB_DIR_LEN);
532       if (! (fp = fopen (path, "r")))
533         continue;
534       pl = mplist__from_file (fp, NULL);
535       fclose (fp);
536       if (! pl)
537         continue;
538       MPLIST_DO (p, pl)
539         {
540           MDatabase mdb;
541           MPlist *p1;
542           int nbytes;
543
544           if (! MPLIST_PLIST_P (p))
545             continue;
546           for (i = 0, p1 = MPLIST_PLIST (p);
547                i < 4 && MPLIST_KEY (p1) == Msymbol;
548                i++, p1 = MPLIST_NEXT (p1))
549             mdb.tag[i] = MPLIST_SYMBOL (p1);
550           if (i == 0
551               || ! MPLIST_MTEXT_P (p1))
552             continue;
553           for (; i < 4; i++)
554             mdb.tag[i] = Mnil;
555           if (mdatabase_find (mdb.tag[0], mdb.tag[1],
556                               mdb.tag[2], mdb.tag[3]))
557             continue;
558
559           mdb.loader = load_database;
560           nbytes = mconv_encode_buffer (Mcoding_utf_8,  MPLIST_MTEXT (p1),
561                                         (unsigned char *) path, PATH_MAX);
562           if (nbytes < 0 || nbytes >= PATH_MAX)
563             continue;
564           path[nbytes++] = '\0';
565           mdb.extra_info = (void *) strdup (path);
566           MLIST_APPEND1 (&mdb_list, mdbs, mdb, MERROR_DB);
567         }
568       M17N_OBJECT_UNREF (pl);
569     }
570
571   mdatabase__finder = ((void *(*) (MSymbol, MSymbol, MSymbol, MSymbol))
572                        mdatabase_find);
573   mdatabase__loader = (void *(*) (void *)) mdatabase_load;
574
575   return 0;
576 }
577
578 void
579 mdatabase__fini (void)
580 {
581   int i;
582   MPlist *plist; 
583
584   MPLIST_DO (plist, mdatabase__dir_list)
585     free (MPLIST_VAL (plist));
586   M17N_OBJECT_UNREF (mdatabase__dir_list);
587
588   for (i = 0; i < mdb_list.used; i++)
589     {
590       MDatabase *mdb = mdb_list.mdbs + i;
591
592       if (mdb->loader == load_database)
593         free (mdb->extra_info);
594     }
595   MLIST_FREE1 (&mdb_list, mdbs);
596 }
597
598 MPlist *
599 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
600 {
601   int mdebug_mask = MDEBUG_DATABASE;
602   FILE *fp;
603   MPlist *plist;
604   char buf[256];
605
606   if (mdb->loader != load_database
607       || mdb->tag[0] == Mchar_table
608       || mdb->tag[0] == Mcharset)
609     MERROR (MERROR_DB, NULL);
610   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
611                  gen_database_name (buf, mdb->tag));
612   fp = get_database_stream ((char *) mdb->extra_info);
613   if (! fp)
614     MERROR (MERROR_DB, NULL);
615   plist = mplist__from_file (fp, keys);
616   fclose (fp);
617   return plist;
618 }
619
620
621 /*** @} */
622 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
623
624 \f
625 /* External API */
626
627 /*** @addtogroup m17nDatabase */
628 /*** @{ */
629
630 /*=*/
631 /***en
632     @brief Directory for application specific data.
633
634     If an application program wants to provide a data specific to the
635     program or a data overriding what supplied by the m17n database,
636     it must set this variable to a name of directory that contains the
637     data files before it calls the macro M17N_INIT ().  The directory
638     may contain a file "mdb.dir" which contains a list of data
639     definitions in the format described in @ref mdbDir "mdbDir(5)".
640
641     The default value is NULL.  */
642 /***ja
643     @brief ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¸ÇÍ­¤Î¥Ç¡¼¥¿Íѥǥ£¥ì¥¯¥È¥ê.
644
645     ¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é¥à¤¬¡¢¤½¤Î¥×¥í¥°¥é¥à¸ÇÍ­¤Î¥Ç¡¼¥¿¤ä m17n ¥Ç¡¼
646     ¥¿¥Ù¡¼¥¹¤ò¾å½ñ¤­¤¹¤ë¥Ç¡¼¥¿¤òÄ󶡤¹¤ë¾ì¹ç¤Ë¤Ï¡¢¥Þ¥¯¥í M17N_INIT () 
647     ¤ò¸Æ¤ÖÁ°¤Ë¤³¤ÎÊÑ¿ô¤ò¥Ç¡¼¥¿¥Õ¥¡¥¤¥ë¤ò´Þ¤à¥Ç¥£¥ì¥¯¥È¥ê̾¤Ë¥»¥Ã¥È¤·¤Ê
648     ¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£¥Ç¥£¥ì¥¯¥È¥ê¤Ë¤Ï "mdb.dir" ¥Õ¥¡¥¤¥ë¤ò¤ª¤¯¤³¤È¤¬¤Ç
649     ¤­¤ë¡£¤½¤Î"mdb.dir"¥Õ¥¡¥¤¥ë¤Ë¤Ï¡¢ @ref mdbDir "mdbDir(5)" ¤ÇÀâÌÀ¤µ
650     ¤ì¤Æ¤¤¤ë¥Õ¥©¡¼¥Þ¥Ã¥È¤Ç¥Ç¡¼¥¿ÄêµÁ¤Î¥ê¥¹¥È¤òµ­½Ò¤¹¤ë¡£
651
652     ¥Ç¥Õ¥©¥ë¥È¤ÎÃͤϠNULL ¤Ç¤¢¤ë¡£  */
653
654 char *mdatabase_dir;
655
656 /*=*/
657 /***en
658     @brief Look for a data in the database.
659
660     The mdatabase_find () function searches the m17n database for a
661     data who has tags $TAG0 through $TAG3, and returns a pointer to
662     the data.  If such a data is not found, it returns @c NULL.  */
663
664 /***ja
665     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹Ãæ¤Î¥Ç¡¼¥¿¤òõ¤¹.
666
667     ´Ø¿ô mdatabase_find () ¤Ï¡¢ m17n ¸À¸ì¾ðÊó¥Ù¡¼¥¹Ãæ¤Ç $TAG0 ¤«¤é 
668     $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤ì¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤½¤Î¤è
669     ¤¦¤Ê¥Ç¡¼¥¿¤¬¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
670
671     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
672
673 MDatabase *
674 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
675 {
676   int i;
677   MDatabaseHookFunc func
678     = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook);
679
680   if (func)
681     func (tag0, tag1, tag2, tag3);
682
683   for (i = 0; i < mdb_list.used; i++)
684     {
685       MDatabase *mdb = mdb_list.mdbs + i;
686
687       if (tag0 == mdb->tag[0]
688           && tag1 == mdb->tag[1]
689           && tag2 == mdb->tag[2]
690           && tag3 == mdb->tag[3])
691         return mdb;
692     }
693   return NULL;
694 }
695
696 /*=*/
697 /***en
698     @brief Return a data list of the m17n database.
699
700     The mdatabase_list () function searches the m17n database for data
701     who have tags $TAG0 through $TAG3, and returns their list by a
702     plist.  The value #Mnil in $TAGn means a wild card that matches
703     any tag.  Each element of the plist has key #Mt and value a
704     pointer to type #MDatabase.  */
705 /***ja
706     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¥ê¥¹¥È¤òÊÖ¤¹.
707
708     ´Ø¿ô mdatabase_list () ¤Ï m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹Ã椫¤é $TAG0 ¤«¤é$TAG3 
709     ¤Þ¤Ç¤Î¥¿¥°¤ò»ý¤Ä¥Ç¡¼¥¿¤òõ¤·¡¢¤½¤Î¥ê¥¹¥È¤òplist ¤È¤·¤ÆÊÖ¤¹¡£ $TAGn 
710     ¤¬ #Mnil ¤Ç¤¢¤Ã¤¿¾ì¹ç¤Ë¤Ï¡¢Ç¤°Õ¤Î¥¿¥°¤Ë¥Þ¥Ã¥Á¤¹¤ë¥ï¥¤¥ë¥É¥«¡¼¥É¤È
711     ¤·¤Æ¼è¤ê°·¤ï¤ì¤ë¡£ÊÖ¤µ¤ì¤ë plist ¤Î³ÆÍ×ÁǤϥ­¡¼ ¤È¤·¤Æ #Mt ¤ò¡¢ÃÍ
712     ¤È¤·¤Æ #MDatabase ·¿¤Ø¤Î¥Ý¥¤¥ó¥¿¤ò»ý¤Ä¡£  */
713
714
715 MPlist *
716 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
717 {
718   int i;
719   MPlist *plist = NULL, *pl;
720   MDatabaseHookFunc func
721     = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook);
722
723   if (func)
724     func (tag0, tag1, tag2, tag3);
725
726   for (i = 0; i < mdb_list.used; i++)
727     {
728       MDatabase *mdb = mdb_list.mdbs + i;
729
730       if ((tag0 == Mnil || tag0 == mdb->tag[0])
731           && (tag1 == Mnil || tag1 == mdb->tag[1])
732           && (tag2 == Mnil || tag2 == mdb->tag[2])
733           && (tag3 == Mnil || tag3 == mdb->tag[3]))
734         {
735           if (! plist)
736             plist = pl = mplist ();
737           pl = mplist_add (pl, Mt, mdb);
738         }
739     }
740   return plist;
741 }
742
743
744
745 /*=*/
746 /***en
747     @brief Define a data of the m17n database.
748
749     The mdatabase_define () function defines a data that has tags
750     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
751
752     $LOADER is a pointer to a function that loads the data from the
753     database.  This function is called from the mdatabase_load ()
754     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
755     $TAGS is the array of $TAG0 through $TAG3.
756
757     If $LOADER is @c NULL, the default loader of the m17n library is
758     used.  In this case, $EXTRA_INFO must be a string specifying a
759     filename that contains the data.
760
761     @return
762     If the operation was successful, mdatabase_define () returns a
763     pointer to the defined data, which can be used as an argument to
764     mdatabase_load ().  Otherwise, it returns @c NULL.  */
765
766 /***ja
767     @brief m17n ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Î¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë.
768
769     ´Ø¿ô mdatabase_define () ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤Î¥¿¥°¤ª¤è¤ÓÉÕ²Ã
770     ¾ðÊó $EXTRA_INFO ¤ò»ý¤Ä¥Ç¡¼¥¿¤òÄêµÁ¤¹¤ë¡£
771
772     $LOADER ¤Ï¤½¤Î¥Ç¡¼¥¿¤Î¥í¡¼¥É¤ËÍѤ¤¤é¤ì¤ë´Ø¿ô¤Ø¤Î¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£¤³
773     ¤Î´Ø¿ô¤Ï mdatabase_load () ¤«¤é $TAGS ¤È $EXTRA_INFO ¤È¤¤¤¦2 ¤Ä¤Î
774     °ú¿ôÉÕ¤­¤Ç¸Æ¤Ó½Ð¤µ¤ì¤ë¡£¤³¤³¤Ç $TAGS ¤Ï $TAG0 ¤«¤é $TAG3 ¤Þ¤Ç¤ÎÇÛ
775     Îó¤Ç¤¢¤ë¡£
776
777     ¤â¤· $LOADER ¤¬ @c NULL ¤Ê¤é¡¢m17n ¥é¥¤¥Ö¥é¥êɸ½à¤Î¥í¡¼¥À¤¬»È¤ï¤ì
778     ¤ë¡£¤³¤Î¾ì¹ç¤Ë¤Ï $EXTRA_INFO ¤Ï¥Ç¡¼¥¿¤ò´Þ¤à¥Õ¥¡¥¤¥ë̾¤Ç¤Ê¤¯¤Æ¤Ï¤Ê
779     ¤é¤Ê¤¤¡£
780
781     @return
782      ½èÍý¤ËÀ®¸ù¤¹¤ì¤Ð mdatabase_define () ¤ÏÄêµÁ¤µ¤ì¤¿¥Ç¡¼¥¿¥Ù¡¼
783     ¥¹¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¤³¤Î¥Ý¥¤¥ó¥¿¤Ï´Ø¿ô mdatabase_load () ¤Î°ú¿ô
784     ¤È¤·¤ÆÍѤ¤¤ë¤³¤È¤¬¤Ç¤­¤ë¡£¤½¤¦¤Ç¤Ê¤±¤ì¤Ð @c NULL ¤òÊÖ¤¹¡£
785
786     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
787
788 /***
789     @seealso
790     mdatabase_load (),  mdatabase_define ()  */
791
792 MDatabase *
793 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
794                   void *(*loader) (MSymbol *, void *),
795                   void *extra_info)
796 {
797   MDatabase *mdb;
798   MDatabaseHookFunc func
799     = (MDatabaseHookFunc) msymbol_get (tag0, M_database_hook);
800
801   if (func)
802     func (tag0, tag1, tag2, tag3);
803
804   mdb = mdatabase_find (tag0, tag1, tag2, tag3);
805   if (! mdb)
806     {
807       MDatabase template;
808
809       template.tag[0] = tag0, template.tag[1] = tag1;
810       template.tag[2] = tag2, template.tag[3] = tag3;
811       template.extra_info = NULL;
812       MLIST_APPEND1 (&mdb_list, mdbs, template, MERROR_DB);
813       mdb = mdb_list.mdbs + (mdb_list.used - 1);
814     }
815   mdb->loader = loader ? loader : load_database;
816   if (mdb->loader == load_database)
817     {
818       if (mdb->extra_info)
819         free (mdb->extra_info);
820       mdb->extra_info = strdup ((char *) extra_info);
821     }
822   else
823     mdb->extra_info = extra_info;
824   return (&(mdb_list.mdbs[mdb_list.used - 1]));
825 }
826
827 /*=*/
828 /***en
829     @brief Load a data from the database.
830
831     The mdatabase_load () function loads a data specified in $MDB and
832     returns the contents.  The type of contents depends on the type of
833     the data.
834
835     If the data is of the @e plist type, this function returns a
836     pointer to @e plist.
837
838     If the database is of the @e chartable type, it returns a
839     chartable.  The default value of the chartable is set according to
840     the second tag of the data as below:
841
842     @li If the tag is #Msymbol, the default value is #Mnil.
843     @li If the tag is #Minteger, the default value is -1.
844     @li Otherwise, the default value is @c NULL.
845
846     If the data is of the @e charset type, it returns a plist of length 2
847     (keys are both #Mt).  The value of the first element is an array
848     of integers that maps code points to the corresponding character
849     codes.  The value of the second element is a chartable of integers
850     that does the reverse mapping.  The charset must be defined in
851     advance.  */
852
853
854 /***ja
855     @brief ¥Ç¡¼¥¿¥Ù¡¼¥¹¤«¤é¥Ç¡¼¥¿¤ò¥í¡¼¥É¤¹¤ë.
856
857     ´Ø¿ô mdatabase_load () ¤Ï $MDB ¤¬»Ø¤¹¥Ç¡¼¥¿¤ò¥í¡¼¥É¤·¡¢¤½¤Î
858     Ãæ¿È¤òÊÖ¤¹¡£ÊÖ¤µ¤ì¤ë¤â¤Î¤Ï¥Ç¡¼¥¿¤Î¥¿¥¤¥×¤Ë¤è¤Ã¤Æ°Û¤Ê¤ë¡£
859
860     ¥Ç¡¼¥¿¤¬ @e plist ¥¿¥¤¥×¤Ê¤é¤Ð¡¢ @e plist ¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£
861
862     ¥Ç¡¼¥¿¤¬ @e chartable ¥¿¥¤¥×¤Ê¤é¤Ðʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£Ê¸»ú¥Æ¡¼¥Ö¥ë
863     ¤Î¥Ç¥Õ¥©¥ë¥ÈÃͤϡ¢¥Ç¡¼¥¿¤ÎÂè2¥¿¥°¤Ë¤è¤Ã¤Æ°Ê²¼¤Î¤è¤¦¤Ë·è¤Þ¤ë¡£
864
865     @li ¥¿¥°¤¬ #Msymbol ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ#Mnil
866     @li ¥¿¥°¤¬ #Minteger ¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ-1
867     @li ¤½¤ì°Ê³°¤Ê¤é¡¢¥Ç¥Õ¥©¥ë¥ÈÃͤϠ@c NULL
868
869     ¥Ç¡¼¥¿¤¬ @e charset ¥¿¥¤¥×¤Ê¤é¤ÐŤµ 2 ¤Î plist ¤òÊÖ¤¹¡Ê¥­¡¼¤Ï¶¦¤Ë 
870     #Mt ¡Ë¡£ºÇ½é¤ÎÍ×ÁǤÎÃͤϥ³¡¼¥É¥Ý¥¤¥ó¥È¤òÂбþ¤¹¤ëʸ»ú¥³¡¼¥É¤Ë¥Þ¥Ã¥×
871     ¤¹¤ëÀ°¿ô¤ÎÇÛÎó¤Ç¤¢¤ë¡££²ÈÖÌܤÎÍ×ÁǤÎÃͤϵդΥޥåפò¤¹¤ëʸ»ú¥Æ¡¼¥Ö
872     ¥ë¤Ç¤¢¤ë¡£¤³¤Îʸ»ú¥»¥Ã¥È¤Ïͽ¤áÄêµÁ¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤é¤Ê¤¤¡£
873
874     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
875   */
876
877 /***
878     @seealso
879     mdatabase_load (),  mdatabase_define ()  */
880
881 void *
882 mdatabase_load (MDatabase *mdb)
883 {
884   int mdebug_mask = MDEBUG_DATABASE;
885   char buf[256];
886
887   MDEBUG_PRINT1 (" [DATABASE] loading <%s>.\n",
888                  gen_database_name (buf, mdb->tag));
889   return (*mdb->loader) (mdb->tag, mdb->extra_info);
890 }
891
892 /*=*/
893 /***en
894     @brief Get tags of a data.
895
896     The mdatabase_tag () function returns an array of tags (symbols)
897     that identify the data in $MDB.  The length of the array is
898     four.  */
899
900 /***ja
901     @brief ¥Ç¡¼¥¿¤Î¥¿¥°¤òÆÀ¤ë.
902
903     ´Ø¿ô mdatabase_tag () ¤Ï¡¢¥Ç¡¼¥¿ $MDB ¤Î¥¿¥°¡Ê¥·¥ó¥Ü¥ë¡Ë¤ÎÇÛÎó¤òÊÖ
904     ¤¹¡£ÇÛÎó¤ÎŤµ¤Ï 4 ¤Ç¤¢¤ë¡£
905
906     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
907
908 MSymbol *
909 mdatabase_tag (MDatabase *mdb)
910 {
911   return mdb->tag;
912 }
913
914 /*** @} */
915
916 /*
917   Local Variables:
918   coding: euc-japan
919   End:
920 */