1 /* database.c -- database module.
2    Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
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    Boston, MA 02110-1301 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-core.h"
140 #include "m17n-misc.h"
141 #include "internal.h"
142 #include "mtext.h"
143 #include "character.h"
144 #include "database.h"
145 #include "plist.h"
146 
147 /** The file containing a list of databases.  */
148 #define MDB_DIR "mdb.dir"
149 /** Length of MDB_DIR.  */
150 #define MDB_DIR_LEN 7
151 
152 #define MAX_TIME(TIME1, TIME2) ((TIME1) >= (TIME2) ? (TIME1) : (TIME2))
153 
154 #define GEN_PATH(path, dir, dir_len, file, file_len)	\
155   (dir_len + file_len > PATH_MAX ? 0			\
156    : (memcpy (path, dir, dir_len),			\
157       memcpy (path + dir_len, file, file_len),		\
158       path[dir_len + file_len] = '\0', 1))
159 
160 static MSymbol Masterisk;
161 static MSymbol Mversion;
162 
163 /** Structure for a data in the m17n database.  */
164 
165 struct MDatabase
166 {
167   /** Tags to identify the data.  <tag>[0] specifies the type of
168       database.  If it is #Mchar_table, the type is @e chartable, if
169       it is #Mcharset, the type is @e charset, otherwise the type is
170       @e plist.  */
171   MSymbol tag[4];
172 
173   void *(*loader) (MSymbol *tags, void *extra_info);
174 
175   /** The meaning of the value is dependent on <loader>.  If <loader>
176       is load_database (), the value is a string of the file name that
177       contains the data.  */
178   void *extra_info;
179 };
180 
181 static MPlist *mdatabase__list;
182 
183 static int
read_number(char * buf,int * i)184 read_number (char *buf, int *i)
185 {
186   int idx = *i;
187   int c = buf[idx++];
188   int n;
189 
190   if (!c)
191     return -1;
192 
193   while (c && isspace (c)) c = buf[idx++];
194 
195   if (c == '0')
196     {
197       if (buf[idx] == 'x')
198 	{
199 	  for (idx++, c = 0; (n = hex_mnemonic[(unsigned) buf[idx]]) < 16;
200 	       idx++)
201 	    c  = (c << 4) | n;
202 	  *i = idx;
203 	  return c;
204 	}
205       c = 0;
206     }
207   else if (c == '\'')
208     {
209       c = buf[idx++];
210       if (c == '\\')
211 	{
212 	  c = buf[idx++];
213 	  n = escape_mnemonic[c];
214 	  if (n != 255)
215 	    c = n;
216 	}
217       while (buf[idx] && buf[idx++] != '\'');
218       *i = idx;
219       return c;
220     }
221   else if (hex_mnemonic[c] < 10)
222     c -= '0';
223   else
224     return -1;
225 
226   while ((n = hex_mnemonic[(unsigned) buf[idx]]) < 10)
227     c = (c * 10) + n, idx++;
228   *i = idx;
229   return c;
230 }
231 
232 
233 /** Load a data of type @c chartable from the file FD, and return the
234     newly created chartable.  */
235 
236 static void *
load_chartable(FILE * fp,MSymbol type)237 load_chartable (FILE *fp, MSymbol type)
238 {
239   int c, from, to;
240   char buf[1024];
241   void *val;
242   MCharTable *table;
243 
244   if (! fp)
245     MERROR (MERROR_DB, NULL);
246 
247   table = mchartable (type, (type == Msymbol ? (void *) Mnil
248 			     : type == Minteger ? (void *) -1
249 			     : NULL));
250 
251   while (! feof (fp))
252     {
253       int i, len;
254 
255       for (len = 0; len < 1023 && (c = getc (fp)) != EOF && c != '\n'; len++)
256 	buf[len] = c;
257       buf[len] = '\0';
258       if (hex_mnemonic[(unsigned) buf[0]] >= 10)
259 	/* skip comment/invalid line */
260 	continue;
261       i = 0;
262       from = read_number (buf, &i);
263       if (buf[i] == '-')
264 	i++, to = read_number (buf, &i);
265       else
266 	to = from;
267       if (from < 0 || to < from)
268 	continue;
269 
270       while (buf[i] && isspace ((unsigned) buf[i])) i++;
271       c = buf[i];
272       if (!c)
273 	continue;
274 
275       if (type == Mstring)
276 	{
277 	  /* VAL is a C-string.  */
278 	  if (! (val = strdup (buf + i)))
279 	    MEMORY_FULL (MERROR_DB);
280 	}
281       else if (type == Minteger)
282 	{
283 	  /* VAL is an integer.  */
284 	  int positive = 1;
285 	  int n;
286 
287 	  if (c == '-')
288 	    i++, positive = -1;
289 	  n = read_number (buf, &i);
290 	  if (n < 0)
291 	    goto label_error;
292 	  val = (void *) (n * positive);
293 	}
294       else if (type == Mtext)
295 	{
296 	  /* VAL is an M-text.  */
297 	  MText *mt;
298 	  if (c == '"')
299 	    mt = mtext__from_data (buf + i, len - i - 1, MTEXT_FORMAT_UTF_8, 1);
300 	  else
301 	    {
302 	      mt = mtext ();
303 	      while ((c = read_number (buf, &i)) >= 0)
304 		mt = mtext_cat_char (mt, c);
305 	    }
306 	  val = (void *) mt;
307 	}
308       else if (type == Msymbol)
309 	{
310 	  char *p = buf + i;
311 
312 	  while (*p && ! isspace (*p))
313 	    {
314 	      if (*p == '\\' && p[1] != '\0')
315 		{
316 		  memmove (p, p + 1, buf + len - (p + 1));
317 		  len--;
318 		}
319 	      p++;
320 	    }
321 	  *p = '\0';
322 	  if (! strcmp (buf + i, "nil"))
323 	    val = (void *) Mnil;
324 	  else
325 	    val = (void *) msymbol (buf + i);
326 	}
327       else if (type == Mplist)
328 	{
329 	  val = (void *) mplist__from_string ((unsigned char *) buf + i,
330 					      strlen (buf + i));
331 	}
332       else
333 	val = NULL;
334 
335       if (from == to)
336 	mchartable_set (table, from, val);
337       else
338 	mchartable_set_range (table, from, to, val);
339     }
340   return table;
341 
342  label_error:
343   M17N_OBJECT_UNREF (table);
344   MERROR (MERROR_DB, NULL);
345 }
346 
347 
348 static char *
gen_database_name(char * buf,MSymbol * tags)349 gen_database_name (char *buf, MSymbol *tags)
350 {
351   int i;
352 
353   strcpy (buf, msymbol_name (tags[0]));
354   for (i = 1; i < 4; i++)
355     {
356       strcat (buf, ",");
357       strcat (buf, msymbol_name (tags[i]));
358     }
359   return buf;
360 }
361 
362 /* Return the absolute file name for DB_INFO->filename or NULL if no
363    absolute file name was found.  If BUF is non-NULL, store the result
364    of `stat' call in it.  In that case, set *RESULT to the return
365    value of `stat'.  */
366 
367 char *
get_database_file(MDatabaseInfo * db_info,struct stat * buf,int * result)368 get_database_file (MDatabaseInfo *db_info, struct stat *buf, int *result)
369 {
370   if (db_info->absolute_filename)
371     {
372       if (buf)
373 	*result = stat (db_info->absolute_filename, buf);
374     }
375   else
376     {
377       struct stat stat_buf;
378       struct stat *statbuf = buf ? buf : &stat_buf;
379       int res;
380       MPlist *plist;
381       char path[PATH_MAX + 1];
382 
383       MPLIST_DO (plist, mdatabase__dir_list)
384 	{
385 	  MDatabaseInfo *dir_info = MPLIST_VAL (plist);
386 
387 	  if (dir_info->status != MDB_STATUS_DISABLED
388 	      && GEN_PATH (path, dir_info->filename, dir_info->len,
389 			   db_info->filename, db_info->len)
390 	      && (res = stat (path, statbuf)) == 0)
391 	    {
392 	      db_info->absolute_filename = strdup (path);
393 	      if (result)
394 		*result = res;
395 	      break;
396 	    }
397 	}
398     }
399 
400   return db_info->absolute_filename;
401 }
402 
403 static void *
load_database(MSymbol * tags,void * extra_info)404 load_database (MSymbol *tags, void *extra_info)
405 {
406   MDatabaseInfo *db_info = extra_info;
407   void *value;
408   char *filename = get_database_file (db_info, NULL, NULL);
409   FILE *fp;
410   int mdebug_flag = MDEBUG_DATABASE;
411   char buf[256];
412 
413   MDEBUG_PRINT1 (" [DB] <%s>", gen_database_name (buf, tags));
414   if (! filename || ! (fp = fopen (filename, "r")))
415     {
416       if (filename)
417 	MDEBUG_PRINT1 (" open fail: %s\n", filename);
418       else
419 	MDEBUG_PRINT1 (" not found: %s\n", db_info->filename);
420       MERROR (MERROR_DB, NULL);
421     }
422 
423   MDEBUG_PRINT1 (" from %s\n", filename);
424 
425   if (tags[0] == Mchar_table)
426     value = load_chartable (fp, tags[1]);
427   else if (tags[0] == Mcharset)
428     {
429       if (! mdatabase__load_charset_func)
430 	MERROR (MERROR_DB, NULL);
431       value = (*mdatabase__load_charset_func) (fp, tags[1]);
432     }
433   else
434     value = mplist__from_file (fp, NULL);
435   fclose (fp);
436 
437   if (! value)
438     MERROR (MERROR_DB, NULL);
439   db_info->time = time (NULL);
440   return value;
441 }
442 
443 
444 /** Return a newly allocated MDatabaseInfo for DIRNAME.  */
445 
446 static MDatabaseInfo *
get_dir_info(char * dirname)447 get_dir_info (char *dirname)
448 {
449   MDatabaseInfo *dir_info;
450 
451   MSTRUCT_CALLOC (dir_info, MERROR_DB);
452   if (dirname)
453     {
454       int len = strlen (dirname);
455 
456       if (len + MDB_DIR_LEN < PATH_MAX)
457 	{
458 	  MTABLE_MALLOC (dir_info->filename, len + 2, MERROR_DB);
459 	  memcpy (dir_info->filename, dirname, len + 1);
460 	  /* Append PATH_SEPARATOR if DIRNAME doesn't end with it.  */
461 	  if (dir_info->filename[len - 1] != PATH_SEPARATOR)
462 	    {
463 	      dir_info->filename[len] = PATH_SEPARATOR;
464 	      dir_info->filename[++len] = '\0';
465 	    }
466 	  dir_info->len = len;
467 	  dir_info->status = MDB_STATUS_OUTDATED;
468 	}
469       else
470 	dir_info->status = MDB_STATUS_DISABLED;
471     }
472   else
473     dir_info->status = MDB_STATUS_DISABLED;
474   return dir_info;
475 }
476 
477 static void register_databases_in_files (MSymbol tags[4],
478 					 char *filename, int len);
479 
480 static MDatabase *
find_database(MSymbol tags[4])481 find_database (MSymbol tags[4])
482 {
483   MPlist *plist;
484   int i;
485   MDatabase *mdb;
486 
487   if (! mdatabase__list)
488     return NULL;
489   for (i = 0, plist = mdatabase__list; i < 4; i++)
490     {
491       MPlist *pl = mplist__assq (plist, tags[i]);
492       MPlist *p;
493 
494       if ((p = mplist__assq (plist, Masterisk)))
495 	{
496 	  MDatabaseInfo *db_info;
497 	  int j;
498 
499 	  p = MPLIST_PLIST (p);
500 	  for (j = i + 1; j < 4; j++)
501 	    p = MPLIST_PLIST (MPLIST_NEXT (p));
502 	  mdb = MPLIST_VAL (MPLIST_NEXT (p));
503 	  db_info = mdb->extra_info;
504 	  if (db_info->status != MDB_STATUS_DISABLED)
505 	    {
506 	      register_databases_in_files (mdb->tag,
507 					   db_info->filename, db_info->len);
508 	      db_info->status = MDB_STATUS_DISABLED;
509 	      return find_database (tags);
510 	    }
511 	}
512       if (! pl)
513 	return NULL;
514       plist = MPLIST_PLIST (pl);
515       plist = MPLIST_NEXT (plist);
516     }
517   mdb = MPLIST_VAL (plist);
518   return mdb;
519 }
520 
521 static void
free_db_info(MDatabaseInfo * db_info)522 free_db_info (MDatabaseInfo *db_info)
523 {
524   free (db_info->filename);
525   if (db_info->absolute_filename
526       && db_info->filename != db_info->absolute_filename)
527     free (db_info->absolute_filename);
528   M17N_OBJECT_UNREF (db_info->properties);
529   free (db_info);
530 }
531 
532 static int
check_version(MText * version)533 check_version (MText *version)
534 {
535   char *verstr = (char *) MTEXT_DATA (version);
536   char *endp = verstr + mtext_nbytes (version);
537   int ver[3];
538   int i;
539 
540   ver[0] = ver[1] = ver[2] = 0;
541   for (i = 0; verstr < endp; verstr++)
542     {
543       if (*verstr == '.')
544 	{
545 	  i++;
546 	  if (i == 3)
547 	    break;
548 	  continue;
549 	}
550       if (! isdigit (*verstr))
551 	break;
552       ver[i] = ver[i] * 10 + (*verstr - '0');
553     }
554   return (ver[0] < M17NLIB_MAJOR_VERSION
555 	  || (ver[0] == M17NLIB_MAJOR_VERSION
556 	      && (ver[1] < M17NLIB_MINOR_VERSION
557 		  || (ver[1] == M17NLIB_MINOR_VERSION
558 		      && ver[2] <= M17NLIB_PATCH_LEVEL))));
559 }
560 
561 static MDatabase *
register_database(MSymbol tags[4],void * (* loader)(MSymbol *,void *),void * extra_info,enum MDatabaseStatus status,MPlist * properties)562 register_database (MSymbol tags[4],
563 		   void *(*loader) (MSymbol *, void *),
564 		   void *extra_info, enum MDatabaseStatus status,
565 		   MPlist *properties)
566 {
567   MDatabase *mdb;
568   MDatabaseInfo *db_info;
569   int i;
570   MPlist *plist;
571 
572   if (properties)
573     {
574       MPLIST_DO (plist, properties)
575 	if (MPLIST_PLIST_P (plist))
576 	  {
577 	    MPlist *p = MPLIST_PLIST (plist);
578 
579 	    if (MPLIST_SYMBOL_P (p)
580 		&& MPLIST_SYMBOL (p) == Mversion
581 		&& MPLIST_MTEXT_P (MPLIST_NEXT (p)))
582 	      {
583 		if (check_version (MPLIST_MTEXT (MPLIST_NEXT (p))))
584 		  break;
585 		return NULL;
586 	      }
587 	  }
588     }
589 
590   for (i = 0, plist = mdatabase__list; i < 4; i++)
591     {
592       MPlist *pl = mplist__assq (plist, tags[i]);
593 
594       if (pl)
595 	pl = MPLIST_PLIST (pl);
596       else
597 	{
598 	  pl = mplist ();
599 	  mplist_add (pl, Msymbol, tags[i]);
600 	  mplist_push (plist, Mplist, pl);
601 	  M17N_OBJECT_UNREF (pl);
602 	}
603       plist = MPLIST_NEXT (pl);
604     }
605 
606   if (MPLIST_TAIL_P (plist))
607     {
608       MSTRUCT_MALLOC (mdb, MERROR_DB);
609       for (i = 0; i < 4; i++)
610 	mdb->tag[i] = tags[i];
611       mdb->loader = loader;
612       if (loader == load_database)
613 	{
614 	  MSTRUCT_CALLOC (db_info, MERROR_DB);
615 	  mdb->extra_info = db_info;
616 	}
617       else
618 	{
619 	  db_info = NULL;
620 	  mdb->extra_info = extra_info;
621 	}
622       mplist_push (plist, Mt, mdb);
623     }
624   else
625     {
626       mdb = MPLIST_VAL (plist);
627       if (loader == load_database)
628 	db_info = mdb->extra_info;
629       else
630 	db_info = NULL;
631     }
632 
633   if (db_info)
634     {
635       db_info->status = status;
636       if (! db_info->filename
637 	  || strcmp (db_info->filename, (char *) extra_info) != 0)
638 	{
639 	  if (db_info->filename)
640 	    free (db_info->filename);
641 	  if (db_info->absolute_filename
642 	      && db_info->filename != db_info->absolute_filename)
643 	    free (db_info->absolute_filename);
644 	  db_info->filename = strdup ((char *) extra_info);
645 	  db_info->len = strlen ((char *) extra_info);
646 	  db_info->time = 0;
647 	}
648       if (db_info->filename[0] == PATH_SEPARATOR)
649 	db_info->absolute_filename = db_info->filename;
650       else
651 	db_info->absolute_filename = NULL;
652       M17N_OBJECT_UNREF (db_info->properties);
653       if (properties)
654 	{
655 	  db_info->properties = properties;
656 	  M17N_OBJECT_REF (properties);
657 	}
658     }
659 
660   if (mdb->tag[0] == Mchar_table
661       && mdb->tag[2] != Mnil
662       && (mdb->tag[1] == Mstring || mdb->tag[1] == Mtext
663 	  || mdb->tag[1] == Msymbol || mdb->tag[1] == Minteger
664 	  || mdb->tag[1] == Mplist))
665     mchar__define_prop (mdb->tag[2], mdb->tag[1], mdb);
666   return mdb;
667 }
668 
669 static void
register_databases_in_files(MSymbol tags[4],char * filename,int len)670 register_databases_in_files (MSymbol tags[4], char *filename, int len)
671 {
672   int i, j;
673   MPlist *load_key = mplist ();
674   FILE *fp;
675   MPlist *plist, *pl;
676 
677   MPLIST_DO (plist, mdatabase__dir_list)
678     {
679       glob_t globbuf;
680       int headlen;
681 
682       if (filename[0] == PATH_SEPARATOR)
683 	{
684 	  if (glob (filename, GLOB_NOSORT, NULL, &globbuf))
685 	    break;
686 	  headlen = 0;
687 	}
688       else
689 	{
690 	  MDatabaseInfo *d_info = MPLIST_VAL (plist);
691 	  char path[PATH_MAX + 1];
692 
693 	  if (d_info->status == MDB_STATUS_DISABLED)
694 	    continue;
695 	  if (! GEN_PATH (path, d_info->filename, d_info->len, filename, len))
696 	    continue;
697 	  if (glob (path, GLOB_NOSORT, NULL, &globbuf))
698 	    continue;
699 	  headlen = d_info->len;
700 	}
701 
702       for (i = 0; i < globbuf.gl_pathc; i++)
703 	{
704 	  if (! (fp = fopen (globbuf.gl_pathv[i], "r")))
705 	    continue;
706 	  pl = mplist__from_file (fp, load_key);
707 	  fclose (fp);
708 	  if (! pl)
709 	    continue;
710 	  if (MPLIST_PLIST_P (pl))
711 	    {
712 	      MPlist *p;
713 	      MSymbol tags2[4];
714 
715 	      for (j = 0, p = MPLIST_PLIST (pl); j < 4 && MPLIST_SYMBOL_P (p);
716 		   j++, p = MPLIST_NEXT (p))
717 		tags2[j] = MPLIST_SYMBOL (p);
718 	      for (; j < 4; j++)
719 		tags2[j] = Mnil;
720 	      for (j = 0; j < 4; j++)
721 		if (tags[j] != Masterisk && tags[j] != tags2[j])
722 		  break;
723 	      if (j == 4)
724 		register_database (tags2, load_database,
725 				   globbuf.gl_pathv[i] + headlen,
726 				   MDB_STATUS_AUTO, p);
727 	    }
728 	  M17N_OBJECT_UNREF (pl);
729 	}
730       globfree (&globbuf);
731       if (filename[0] == PATH_SEPARATOR)
732 	break;
733     }
734   M17N_OBJECT_UNREF (load_key);
735 }
736 
737 static int
expand_wildcard_database(MPlist * plist)738 expand_wildcard_database (MPlist *plist)
739 {
740   MDatabase *mdb;
741   MDatabaseInfo *db_info;
742 
743   plist = MPLIST_NEXT (plist);
744   while (MPLIST_PLIST_P (plist))
745     {
746       plist = MPLIST_PLIST (plist);
747       plist = MPLIST_NEXT (plist);
748     }
749   mdb = MPLIST_VAL (plist);
750   if (mdb->loader == load_database
751       && (db_info = mdb->extra_info)
752       && db_info->status != MDB_STATUS_DISABLED)
753     {
754       register_databases_in_files (mdb->tag, db_info->filename, db_info->len);
755       db_info->status = MDB_STATUS_DISABLED;
756       return 1;
757     }
758   return 0;
759 }
760 
761 
762 /* Internal API */
763 
764 /** List of database directories.  */
765 MPlist *mdatabase__dir_list;
766 
767 void *(*mdatabase__load_charset_func) (FILE *fp, MSymbol charset_name);
768 
769 int
mdatabase__init()770 mdatabase__init ()
771 {
772   MDatabaseInfo *dir_info;
773   char *path;
774 
775   mdatabase__load_charset_func = NULL;
776 
777   Mchar_table = msymbol ("char-table");
778   Mcharset = msymbol ("charset");
779   Masterisk = msymbol ("*");
780   Mversion = msymbol ("version");
781 
782   mdatabase__dir_list = mplist ();
783   /** The macro M17NDIR specifies a directory where the system-wide
784     MDB_DIR file exists.  */
785   mplist_set (mdatabase__dir_list, Mt, get_dir_info (M17NDIR));
786 
787   /* The variable mdatabase_dir specifies a directory where an
788      application program specific MDB_DIR file exists.  */
789   if (mdatabase_dir && strlen (mdatabase_dir) > 0)
790     mplist_push (mdatabase__dir_list, Mt, get_dir_info (mdatabase_dir));
791 
792   /* The environment variable M17NDIR specifies a directory where a
793      user specific MDB_DIR file exists.  */
794   path = getenv ("M17NDIR");
795   if (path && strlen (path) > 0)
796     mplist_push (mdatabase__dir_list, Mt, get_dir_info (path));
797   else
798     {
799       /* If the env var M17NDIR is not set, check "~/.m17n.d".  */
800       char *home = getenv ("HOME");
801       int len;
802 
803       if (home
804 	  && (len = strlen (home))
805 	  && (path = alloca (len + 9)))
806 	{
807 	  strcpy (path, home);
808 	  if (path[len - 1] != PATH_SEPARATOR)
809 	    path[len++] = PATH_SEPARATOR;
810 	  strcpy (path + len, ".m17n.d");
811 	  dir_info = get_dir_info (path);
812 	  mplist_push (mdatabase__dir_list, Mt, dir_info);
813 	}
814       else
815 	mplist_push (mdatabase__dir_list, Mt, get_dir_info (NULL));
816     }
817 
818   mdatabase__list = mplist ();
819   mdatabase__update ();
820   return 0;
821 }
822 
823 void
mdatabase__fini(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
mdatabase__update(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] = with_wildcard ? Masterisk : 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 	    register_database (tags, load_database, path,
987 			       MDB_STATUS_AUTO_WILDCARD, NULL);
988 	  else
989 	    register_database (tags, load_database, path,
990 			       MDB_STATUS_AUTO, p1);
991 	}
992       M17N_OBJECT_UNREF (pl);
993     }
994   M17N_OBJECT_UNREF (plist);
995 }
996 
997 MPlist *
mdatabase__load_for_keys(MDatabase * mdb,MPlist * keys)998 mdatabase__load_for_keys (MDatabase *mdb, MPlist *keys)
999 {
1000   int mdebug_flag = MDEBUG_DATABASE;
1001   MDatabaseInfo *db_info;
1002   char *filename;
1003   FILE *fp;
1004   MPlist *plist;
1005   char name[256];
1006 
1007   if (mdb->loader != load_database
1008       || mdb->tag[0] == Mchar_table
1009       || mdb->tag[0] == Mcharset)
1010     MERROR (MERROR_DB, NULL);
1011   MDEBUG_PRINT1 (" [DB]  <%s>.\n",
1012 		 gen_database_name (name, mdb->tag));
1013   db_info = mdb->extra_info;
1014   filename = get_database_file (db_info, NULL, NULL);
1015   if (! filename || ! (fp = fopen (filename, "r")))
1016     MERROR (MERROR_DB, NULL);
1017   plist = mplist__from_file (fp, keys);
1018   fclose (fp);
1019   return plist;
1020 }
1021 
1022 
1023 /* Check if the database MDB should be reloaded or not.  It returns:
1024 
1025 	1: The database has not been updated since it was loaded last
1026 	time.
1027 
1028 	0: The database has never been loaded or has been updated
1029 	since it was loaded last time.
1030 
1031 	-1: The database is not loadable at the moment.  */
1032 
1033 int
mdatabase__check(MDatabase * mdb)1034 mdatabase__check (MDatabase *mdb)
1035 {
1036   MDatabaseInfo *db_info = (MDatabaseInfo *) mdb->extra_info;
1037   struct stat buf;
1038   int result;
1039 
1040   if (db_info->absolute_filename != db_info->filename
1041       || db_info->status == MDB_STATUS_AUTO)
1042     mdatabase__update ();
1043 
1044   if (! get_database_file (db_info, &buf, &result)
1045       || result < 0)
1046     return -1;
1047   if (db_info->time < buf.st_mtime)
1048     return 0;
1049   return 1;
1050 }
1051 
1052 /* Search directories in mdatabase__dir_list for file FILENAME.  If
1053    the file exist, return the absolute pathname.  If FILENAME is
1054    already absolute, return a copy of it.  */
1055 
1056 char *
mdatabase__find_file(char * filename)1057 mdatabase__find_file (char *filename)
1058 {
1059   struct stat buf;
1060   int result;
1061   MDatabaseInfo db_info;
1062 
1063   if (filename[0] == PATH_SEPARATOR)
1064     return (stat (filename, &buf) == 0 ? strdup (filename) : NULL);
1065   db_info.filename = filename;
1066   db_info.len = strlen (filename);
1067   db_info.time = 0;
1068   db_info.absolute_filename = NULL;
1069   if (! get_database_file (&db_info, &buf, &result)
1070       || result < 0)
1071     return NULL;
1072   return db_info.absolute_filename;
1073 }
1074 
1075 char *
mdatabase__file(MDatabase * mdb)1076 mdatabase__file (MDatabase *mdb)
1077 {
1078   MDatabaseInfo *db_info;
1079 
1080   if (mdb->loader != load_database)
1081     return NULL;
1082   db_info = mdb->extra_info;
1083   return get_database_file (db_info, NULL, NULL);
1084 }
1085 
1086 int
mdatabase__lock(MDatabase * mdb)1087 mdatabase__lock (MDatabase *mdb)
1088 {
1089   MDatabaseInfo *db_info;
1090   struct stat buf;
1091   FILE *fp;
1092   int len;
1093   char *file;
1094 
1095   if (mdb->loader != load_database)
1096     return -1;
1097   db_info = mdb->extra_info;
1098   if (db_info->lock_file)
1099     return -1;
1100   file = get_database_file (db_info, NULL, NULL);
1101   if (! file)
1102     return -1;
1103   len = strlen (file);
1104   db_info->uniq_file = malloc (len + 35);
1105   if (! db_info->uniq_file)
1106     return -1;
1107   db_info->lock_file = malloc (len + 5);
1108   if (! db_info->lock_file)
1109     {
1110       free (db_info->uniq_file);
1111       return -1;
1112     }
1113   sprintf (db_info->uniq_file, "%s.%X.%X", db_info->absolute_filename,
1114 	   (unsigned) time (NULL), (unsigned) getpid ());
1115   sprintf (db_info->lock_file, "%s.LCK", db_info->absolute_filename);
1116 
1117   fp = fopen (db_info->uniq_file, "w");
1118   if (! fp)
1119     {
1120       char *str = strdup (db_info->uniq_file);
1121       char *dir = dirname (str);
1122 
1123       if (stat (dir, &buf) == 0
1124 	  || mkdir (dir, 0777) < 0
1125 	  || ! (fp = fopen (db_info->uniq_file, "w")))
1126 	{
1127 	  free (db_info->uniq_file);
1128 	  free (db_info->lock_file);
1129 	  db_info->lock_file = NULL;
1130 	  free (str);
1131 	  return -1;
1132 	}
1133       free (str);
1134     }
1135   fclose (fp);
1136   if (link (db_info->uniq_file, db_info->lock_file) < 0
1137       && (stat (db_info->uniq_file, &buf) < 0
1138 	  || buf.st_nlink != 2))
1139     {
1140       unlink (db_info->uniq_file);
1141       unlink (db_info->lock_file);
1142       free (db_info->uniq_file);
1143       free (db_info->lock_file);
1144       db_info->lock_file = NULL;
1145       return 0;
1146     }
1147   return 1;
1148 }
1149 
1150 int
mdatabase__save(MDatabase * mdb,MPlist * data)1151 mdatabase__save (MDatabase *mdb, MPlist *data)
1152 {
1153   MDatabaseInfo *db_info;
1154   FILE *fp;
1155   char *file;
1156   MText *mt;
1157   int ret;
1158 
1159   if (mdb->loader != load_database)
1160     return -1;
1161   db_info = mdb->extra_info;
1162   if (! db_info->lock_file)
1163     return -1;
1164   file = get_database_file (db_info, NULL, NULL);
1165   if (! file)
1166     return -1;
1167   mt = mtext ();
1168   if (mplist__serialize (mt, data, 1) < 0)
1169     {
1170       M17N_OBJECT_UNREF (mt);
1171       return -1;
1172     }
1173   fp = fopen (db_info->uniq_file, "w");
1174   if (! fp)
1175     {
1176       M17N_OBJECT_UNREF (mt);
1177       return -1;
1178     }
1179   if (mt->format > MTEXT_FORMAT_UTF_8)
1180     mtext__adjust_format (mt, MTEXT_FORMAT_UTF_8);
1181   fwrite (MTEXT_DATA (mt), 1, mtext_nchars (mt), fp);
1182   fclose (fp);
1183   M17N_OBJECT_UNREF (mt);
1184   if ((ret = rename (db_info->uniq_file, file)) < 0)
1185     unlink (db_info->uniq_file);
1186   free (db_info->uniq_file);
1187   db_info->uniq_file = NULL;
1188   return ret;
1189 }
1190 
1191 int
mdatabase__unlock(MDatabase * mdb)1192 mdatabase__unlock (MDatabase *mdb)
1193 {
1194   MDatabaseInfo *db_info;
1195 
1196   if (mdb->loader != load_database)
1197     return -1;
1198   db_info = mdb->extra_info;
1199   if (! db_info->lock_file)
1200     return -1;
1201   unlink (db_info->lock_file);
1202   free (db_info->lock_file);
1203   db_info->lock_file = NULL;
1204   if (db_info->uniq_file)
1205     {
1206       unlink (db_info->uniq_file);
1207       free (db_info->uniq_file);
1208     }
1209   return 0;
1210 }
1211 
1212 MPlist *
mdatabase__props(MDatabase * mdb)1213 mdatabase__props (MDatabase *mdb)
1214 {
1215   MDatabaseInfo *db_info;
1216 
1217   if (mdb->loader != load_database)
1218     return NULL;
1219   db_info = mdb->extra_info;
1220   return db_info->properties;
1221 }
1222 
1223 /*** @} */
1224 #endif /* !FOR_DOXYGEN || DOXYGEN_INTERNAL_MODULE */
1225 
1226 
1227 /* External API */
1228 
1229 /*** @addtogroup m17nCharset */
1230 /*** @{ */
1231 /*=*/
1232 /***en
1233     @brief The symbol @c Mcharset.
1234 
1235     Any decoded M-text has a text property whose key is the predefined
1236     symbol @c Mcharset.  The name of @c Mcharset is
1237     <tt>"charset"</tt>.  */
1238 
1239 /***ja
1240     @brief ����ܥ� @c Mcharset.
1241 
1242     �ǥ����ɤ��줿 M-text �ϡ������� @c Mcharset
1243     �Ǥ���褦�ʥƥ����ȥץ�ѥƥ�����ġ�
1244     ����ܥ� @c Mcharset �� <tt>"charset"</tt> �Ȥ���̾������ġ�  */
1245 
1246 MSymbol Mcharset;
1247 /*=*/
1248 /*** @} */
1249 /*=*/
1250 
1251 /*** @addtogroup m17nDatabase */
1252 /*** @{ */
1253 
1254 /*=*/
1255 /***en
1256     @brief Directory for application specific data.
1257 
1258     If an application program wants to provide a data specific to the
1259     program or a data overriding what supplied by the m17n database,
1260     it must set this variable to a name of directory that contains the
1261     data files before it calls the macro M17N_INIT ().  The directory
1262     may contain a file "mdb.dir" which contains a list of data
1263     definitions in the format described in @ref mdbDir "mdbDir(5)".
1264 
1265     The default value is NULL.  */
1266 /***ja
1267     @brief ���ץꥱ��������ͭ�Υǡ����ѥǥ��쥯�ȥ�.
1268 
1269     ���ץꥱ�������ץ���ब�����Υץ�����ͭ�Υǡ����� m17n
1270     �ǡ����١�����������ǡ�������������ˤϡ��ޥ��� M17N_INIT ()
1271     ��Ƥ����ˤ����ѿ���ǡ����ե������ޤ�ǥ��쥯�ȥ�̾�˥��åȤ��ʤ��ƤϤʤ�ʤ����ǥ��쥯�ȥ�ˤ�
1272     "mdb.dir" �ե�����������Ȥ��Ǥ��롣����"mdb.dir"�ե�����ˤϡ�
1273     @ref mdbDir "mdbDir(5)" ����������Ƥ���ե����ޥåȤǥǡ�������Υꥹ�Ȥ��Ҥ��롣
1274 
1275     �ǥե���Ȥ��ͤ� NULL �Ǥ��롣  */
1276 
1277 char *mdatabase_dir;
1278 
1279 /*=*/
1280 /***en
1281     @brief Look for a data in the database.
1282 
1283     The mdatabase_find () function searches the m17n database for a
1284     data who has tags $TAG0 through $TAG3, and returns a pointer to
1285     the data.  If such a data is not found, it returns @c NULL.  */
1286 
1287 /***ja
1288     @brief �ǡ����١�����Υǡ�����õ��.
1289 
1290     �ؿ� mdatabase_find () �ϡ� m17n �������١������ $TAG0 ����
1291     $TAG3 �ޤǤΥ�������ĥǡ�����õ��������ؤΥݥ������֤������Τ褦�ʥǡ������ʤ����
1292     @c NULL ���֤���
1293 
1294     @latexonly \IPAlabel{mdatabase_find} @endlatexonly  */
1295 
1296 MDatabase *
mdatabase_find(MSymbol tag0,MSymbol tag1,MSymbol tag2,MSymbol tag3)1297 mdatabase_find (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1298 {
1299   MSymbol tags[4];
1300 
1301   mdatabase__update ();
1302   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1303   return find_database (tags);
1304 }
1305 
1306 /*=*/
1307 /***en
1308     @brief Return a data list of the m17n database.
1309 
1310     The mdatabase_list () function searches the m17n database for data
1311     who have tags $TAG0 through $TAG3, and returns their list by a
1312     plist.  The value #Mnil in $TAGn means a wild card that matches
1313     any tag.  Each element of the plist has key #Mt and value a
1314     pointer to type #MDatabase.  */
1315 /***ja
1316     @brief m17n �ǡ����١����Υǡ����ꥹ�Ȥ��֤�.
1317 
1318     �ؿ� mdatabase_list () �� m17n �ǡ����١����椫�� $TAG0 ����$TAG3
1319     �ޤǤΥ�������ĥǡ�����õ�������Υꥹ�Ȥ�plist �Ȥ����֤��� $TAGn �� #Mnil
1320     �Ǥ��ä����ˤϡ�Ǥ�դΥ����˥ޥå�����磻��ɥ����ɤȤ��Ƽ�갷���롣�֤����
1321     plist �γ����Ǥϥ��� �Ȥ��� #Mt ���ͤȤ��� #MDatabase ���ؤΥݥ�������ġ�  */
1322 
1323 MPlist *
mdatabase_list(MSymbol tag0,MSymbol tag1,MSymbol tag2,MSymbol tag3)1324 mdatabase_list (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
1325 {
1326   MPlist *plist = mplist (), *pl = plist;
1327   MPlist *p, *p0, *p1, *p2, *p3;
1328 
1329   mdatabase__update ();
1330 
1331   MPLIST_DO (p, mdatabase__list)
1332     {
1333       p0 = MPLIST_PLIST (p);
1334       /* P0 ::= (TAG0 (TAG1 (TAG2 (TAG3 MDB) ...) ...) ...) */
1335       if (MPLIST_SYMBOL (p0) == Masterisk
1336 	  || (tag0 != Mnil && MPLIST_SYMBOL (p0) != tag0))
1337 	continue;
1338       MPLIST_DO (p0, MPLIST_NEXT (p0))
1339 	{
1340 	  p1 = MPLIST_PLIST (p0);
1341 	  if (MPLIST_SYMBOL (p1) == Masterisk)
1342 	    {
1343 	      if (expand_wildcard_database (p1))
1344 		{
1345 		  M17N_OBJECT_UNREF (plist);
1346 		  return mdatabase_list (tag0, tag1, tag2, tag3);
1347 		}
1348 	      continue;
1349 	    }
1350 	  if (tag1 != Mnil && MPLIST_SYMBOL (p1) != tag1)
1351 	    continue;
1352 	  MPLIST_DO (p1, MPLIST_NEXT (p1))
1353 	    {
1354 	      p2 = MPLIST_PLIST (p1);
1355 	      if (MPLIST_SYMBOL (p2) == Masterisk)
1356 		{
1357 		  if (expand_wildcard_database (p2))
1358 		    {
1359 		      M17N_OBJECT_UNREF (plist);
1360 		      return mdatabase_list (tag0, tag1, tag2, tag3);
1361 		    }
1362 		  continue;
1363 		}
1364 	      if (tag2 != Mnil && MPLIST_SYMBOL (p2) != tag2)
1365 		continue;
1366 	      MPLIST_DO (p2, MPLIST_NEXT (p2))
1367 		{
1368 		  p3 = MPLIST_PLIST (p2);
1369 		  if (MPLIST_SYMBOL (p3) == Masterisk)
1370 		    {
1371 		      if (expand_wildcard_database (p3))
1372 			{
1373 			  M17N_OBJECT_UNREF (plist);
1374 			  return mdatabase_list (tag0, tag1, tag2, tag3);
1375 			}
1376 		      continue;
1377 		    }
1378 		  if (tag3 != Mnil && MPLIST_SYMBOL (p3) != tag3)
1379 		    continue;
1380 		  p3 = MPLIST_NEXT (p3);
1381 		  pl = mplist_add (pl, Mt, MPLIST_VAL (p3));
1382 		}
1383 	    }
1384 	}
1385     }
1386   if (MPLIST_TAIL_P (plist))
1387     M17N_OBJECT_UNREF (plist);
1388   return plist;
1389 }
1390 
1391 /*=*/
1392 /***en
1393     @brief Define a data of the m17n database.
1394 
1395     The mdatabase_define () function defines a data that has tags
1396     $TAG0 through $TAG3 and additional information $EXTRA_INFO.
1397 
1398     $LOADER is a pointer to a function that loads the data from the
1399     database.  This function is called from the mdatabase_load ()
1400     function with the two arguments $TAGS and $EXTRA_INFO.  Here,
1401     $TAGS is the array of $TAG0 through $TAG3.
1402 
1403     If $LOADER is @c NULL, the default loader of the m17n library is
1404     used.  In this case, $EXTRA_INFO must be a string specifying a
1405     filename that contains the data.
1406 
1407     @return
1408     If the operation was successful, mdatabase_define () returns a
1409     pointer to the defined data, which can be used as an argument to
1410     mdatabase_load ().  Otherwise, it returns @c NULL.  */
1411 
1412 /***ja
1413     @brief m17n �ǡ����١����Υǡ������������.
1414 
1415     �ؿ� mdatabase_define () �� $TAG0 ���� $TAG3 �ޤǤΥ���������ղþ���
1416     $EXTRA_INFO ����ĥǡ�����������롣
1417 
1418     $LOADER �Ϥ��Υǡ����Υ��ɤ��Ѥ�����ؿ��ؤΥݥ����Ǥ��롣���δؿ���
1419     mdatabase_load () ���� $TAGS �� $EXTRA_INFO �Ȥ�����Ĥΰ����դ��ǸƤӽФ���롣������
1420     $TAGS �� $TAG0 ���� $TAG3 �ޤǤ�����Ǥ��롣
1421 
1422     �⤷ $LOADER �� @c NULL �ʤ顢m17n �饤�֥��ɸ��Υ������Ȥ��롣���ξ��ˤ�
1423     $EXTRA_INFO �ϥǡ�����ޤ�ե�����̾�Ǥʤ��ƤϤʤ�ʤ���
1424 
1425     @return
1426     ��������������� mdatabase_define ()
1427     ��������줿�ǡ����١����ؤΥݥ������֤������Υݥ����ϴؿ� mdatabase_load ()
1428     �ΰ����Ȥ����Ѥ��뤳�Ȥ��Ǥ��롣�����Ǥʤ���� @c NULL ���֤���
1429 
1430     @latexonly \IPAlabel{mdatabase_define} @endlatexonly  */
1431 
1432 /***
1433     @seealso
1434     mdatabase_load (),  mdatabase_define ()  */
1435 
1436 MDatabase *
mdatabase_define(MSymbol tag0,MSymbol tag1,MSymbol tag2,MSymbol tag3,void * (* loader)(MSymbol *,void *),void * extra_info)1437 mdatabase_define (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3,
1438 		  void *(*loader) (MSymbol *, void *),
1439 		  void *extra_info)
1440 {
1441   MDatabase *mdb;
1442   MSymbol tags[4];
1443 
1444   tags[0] = tag0, tags[1] = tag1, tags[2] = tag2, tags[3] = tag3;
1445   if (! loader)
1446     loader = load_database;
1447   mdb = register_database (tags, loader, extra_info, MDB_STATUS_EXPLICIT, NULL);
1448   return mdb;
1449 }
1450 
1451 /*=*/
1452 /***en
1453     @brief Load a data from the database.
1454 
1455     The mdatabase_load () function loads a data specified in $MDB and
1456     returns the contents.  The type of contents depends on the type of
1457     the data.
1458 
1459     If the data is of the @e plist @e type, this function returns a
1460     pointer to @e plist.
1461 
1462     If the database is of the @e chartable @e type, it returns a
1463     chartable.  The default value of the chartable is set according to
1464     the second tag of the data as below:
1465 
1466     @li If the tag is #Msymbol, the default value is #Mnil.
1467     @li If the tag is #Minteger, the default value is -1.
1468     @li Otherwise, the default value is @c NULL.
1469 
1470     If the data is of the @e charset @e type, it returns a plist of length 2
1471     (keys are both #Mt).  The value of the first element is an array
1472     of integers that maps code points to the corresponding character
1473     codes.  The value of the second element is a chartable of integers
1474     that does the reverse mapping.  The charset must be defined in
1475     advance.  */
1476 
1477 
1478 /***ja
1479     @brief �ǡ����١�������ǡ�������ɤ���.
1480 
1481     �ؿ� mdatabase_load () �� $MDB
1482     ���ؤ��ǡ�������ɤ���������Ȥ��֤����֤�����Τϥǡ����Υ����פˤ�äưۤʤ롣
1483 
1484     �ǡ����� @e plist������ �ʤ�С� @e plist �ؤΥݥ������֤���
1485 
1486     �ǡ����� @e chartable������ �ʤ��ʸ���ơ��֥���֤���
1487     ʸ���ơ��֥�Υǥե�����ͤϡ��ǡ�������2�����ˤ�äưʲ��Τ褦�˷�ޤ롣
1488 
1489     @li ������ #Msymbol �ʤ顢�ǥե�����ͤ� #Mnil
1490     @li ������ #Minteger �ʤ顢�ǥե�����ͤ� -1
1491     @li ����ʳ��ʤ顢�ǥե�����ͤ� @c NULL
1492 
1493     �ǡ����� @e charset������ �ʤ��Ĺ�� 2 �� plist ���֤��ʥ����϶���#Mt �ˡ�
1494     �ǽ�����Ǥ��ͤϥ����ɥݥ���Ȥ��б�����ʸ�������ɤ˥ޥåפ�������������Ǥ��롣
1495     �����ܤ����Ǥ��ͤϵդΥޥåפ���ʸ���ơ��֥�Ǥ��롣
1496     ����ʸ�����åȤ�ͽ���������Ƥ��ʤ���Фʤ�ʤ���
1497 
1498     @latexonly \IPAlabel{mdatabase_load} @endlatexonly
1499   */
1500 
1501 /***
1502     @seealso
1503     mdatabase_load (),  mdatabase_define ()  */
1504 
1505 void *
mdatabase_load(MDatabase * mdb)1506 mdatabase_load (MDatabase *mdb)
1507 {
1508   return (*mdb->loader) (mdb->tag, mdb->extra_info);
1509 }
1510 
1511 /*=*/
1512 /***en
1513     @brief Get tags of a data.
1514 
1515     The mdatabase_tag () function returns an array of tags (symbols)
1516     that identify the data in $MDB.  The length of the array is
1517     four.  */
1518 
1519 /***ja
1520     @brief �ǡ����Υ���������.
1521 
1522     �ؿ� mdatabase_tag () �ϡ��ǡ��� $MDB �Υ����ʥ���ܥ�ˤ�������֤��������Ĺ����
1523     4 �Ǥ��롣
1524 
1525     @latexonly \IPAlabel{mdatabase_tag} @endlatexonly  */
1526 
1527 MSymbol *
mdatabase_tag(MDatabase * mdb)1528 mdatabase_tag (MDatabase *mdb)
1529 {
1530   return mdb->tag;
1531 }
1532 
1533 /*** @} */
1534 
1535 /*
1536   Local Variables:
1537   coding: euc-japan
1538   End:
1539 */
1540