(struct CHISE_DS): Add new member `subtype' and `modemask'.
[chise/libchise.git] / chise.c
1 #ifdef HAVE_CONFIG_H
2 #  include "config.h"
3 #endif
4
5 #ifndef HAVE_STRNLEN
6 /* original in mysql, strings/strnlen.c.
7 uint strnlen(register const char *s, register uint maxlen)
8 {
9   const char *end= (const char *)memchr(s, '\0', maxlen);
10   return end ? (uint) (end - s) : maxlen;
11 }
12 */
13 static inline int
14 strnlen (register const char *s, register int maxlen)
15 {
16   const char *end= (const char *)memchr(s, '\0', maxlen);
17   return end ? (int) (end - s) : maxlen;
18 }
19 #endif
20
21 #include "chise.h"
22 #include "chise-name.h"
23
24
25 CHISE_Feature_Table*
26 chise_ds_open_feature_table (CHISE_DS *ds, const char *feature);
27
28 int chise_ft_close (CHISE_Feature_Table *table);
29
30 CHISE_CCS_Table*
31 chise_ds_open_ccs_table (CHISE_DS *ds, const char *ccs);
32
33 int chise_ccst_close (CHISE_CCS_Table *table);
34
35
36 typedef DB CHISE_Attribute_Table;
37
38 CHISE_Attribute_Table*
39 chise_open_attribute_table (const unsigned char *db_dir,
40                             const char *encoding, const char *feature,
41                             DBTYPE real_subtype,
42                             u_int32_t accessmask, int modemask);
43
44 int chise_close_attribute_table (CHISE_Attribute_Table *db);
45
46 int chise_get_attribute_table (CHISE_Attribute_Table *db,
47                                char *key, CHISE_Value *valdatum);
48
49 int chise_put_attribute_table (CHISE_Attribute_Table *db,
50                                char *key, unsigned char *value);
51
52
53 #define xzero(lvalue) ((void) memset (&(lvalue), '\0', sizeof (lvalue)))
54
55 CHISE_Char_ID
56 chise_char_id_parse_c_string (unsigned char *str, int len)
57 {
58   int i = 0;
59
60   if ( (len >= 2) && (str[i++] == '?') )
61     {
62       unsigned char c = str[i++];
63       int counter;
64       CHISE_Char_ID cid;
65
66       if (c == '\\')
67         {
68           if (len < 3)
69             return -1;
70           c = str[i++];
71           if (c == '^')
72             {
73               if (len < 4)
74                 return -1;
75               c = str[i++];
76               if (c == '?')
77                 return 0x7F;
78               else
79                 return c & (0x80 | 0x1F);
80             }
81         }
82       if ( c < 0xC0 )
83         {
84           cid = c;
85           counter = 0;
86         }
87       else if ( c < 0xE0 )
88         {
89           cid = c & 0x1f;
90           counter = 1;
91         }
92       else if ( c < 0xF0 )
93         {
94           cid = c & 0x0f;
95           counter = 2;
96         }
97       else if ( c < 0xF8 )
98         {
99           cid = c & 0x07;
100           counter = 3;
101         }
102       else if ( c < 0xFC )
103         {
104           cid = c & 0x03;
105           counter = 4;
106         }
107       else
108         {
109           cid = c & 0x01;
110           counter = 5;
111         }
112
113       if (counter + 2 <= len)
114         {
115           int j;
116
117           for (j = 0; j < counter; j++)
118             cid = (cid << 6) | (str[j + i] & 0x3F);
119           return cid;
120         }
121     }
122   return -1;
123 }
124
125 int
126 chise_format_char_id (CHISE_Char_ID cid, unsigned char *dest, int len)
127 {
128   int i = 0;
129
130   dest[i++] = '?';
131   if (cid == '\t')
132     {
133       dest[i++] = '\\';
134       dest[i++] = 't';
135       dest[i] = '\0';
136       return i;
137     }
138   else if (cid == '\n')
139     {
140       dest[i++] = '\\';
141       dest[i++] = 'n';
142       dest[i] = '\0';
143       return i;
144     }
145   else if (cid == '\r')
146     {
147       dest[i++] = '\\';
148       dest[i++] = 'r';
149       dest[i] = '\0';
150       return i;
151     }
152   else if (cid == 0x1C)
153     {
154       dest[i++] = '\\';
155       dest[i++] = '^';
156       dest[i++] = '\\';
157       dest[i++] = '\\';
158       dest[i] = '\0';
159       return i;
160     }
161   else if (cid <= 0x1F)
162     {
163       dest[i++] = '\\';
164       dest[i++] = '^';
165       dest[i++] = '@' + cid;
166       dest[i] = '\0';
167       return i;
168     }
169   else if ( (cid == ' ') || (cid == '"') ||
170             (cid == '#') || (cid == '\'') ||
171             (cid == '(') || (cid == ')') ||
172             (cid == ',') || (cid == '.') ||
173             (cid == ';') || (cid == '?') ||
174             (cid == '[') || (cid == '\\') ||
175             (cid == ']') || (cid == '`') )
176     {
177       dest[i++] = '\\';
178       dest[i++] = cid;
179       dest[i] = '\0';
180       return i;
181     }
182   else if (cid <= 0x7E)
183     {
184       dest[i++] = cid;
185       dest[i] = '\0';
186       return i;
187     }
188   else if (cid == 0x7F)
189     {
190       dest[i++] = '\\';
191       dest[i++] = '^';
192       dest[i++] = '?';
193       dest[i] = '\0';
194       return i;
195     }
196   else if (cid <= 0x9F)
197     {
198       dest[i++] = '\\';
199       dest[i++] = '^';
200       dest[i++] = ((cid + '@') >> 6) | 0xC0;
201       dest[i++] = ((cid + '@') & 0x3F) | 0x80;
202       dest[i] = '\0';
203       return i;
204     }
205   else if (cid <= 0x7FF)
206     {
207       dest[i++] = (cid >> 6) | 0xC0;
208       dest[i++] = (cid & 0x3F) | 0x80;
209       dest[i] = '\0';
210       return i;
211     }
212   else if (cid <= 0xFFFF)
213     {
214       dest[i++] = (cid >> 12) | 0xE0;
215       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
216       dest[i++]=  (cid        & 0x3F) | 0x80;
217       dest[i] = '\0';
218       return i;
219     }
220   else if (cid <= 0x1FFFFF)
221     {
222       dest[i++]=  (cid >> 18) | 0xF0;
223       dest[i++]= ((cid >> 12) & 0x3F) | 0x80;
224       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
225       dest[i++]=  (cid        & 0x3F) | 0x80;
226       dest[i] = '\0';
227       return i;
228     }
229   else if (cid <= 0x3FFFFFF)
230     {
231       dest[i++]=  (cid >> 24) | 0xF8;
232       dest[i++]= ((cid >> 18) & 0x3F) | 0x80;
233       dest[i++]= ((cid >> 12) & 0x3F) | 0x80;
234       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
235       dest[i++]=  (cid        & 0x3F) | 0x80;
236       dest[i] = '\0';
237       return i;
238     }
239   else
240     {
241       dest[i++]=  (cid >> 30) | 0xFC;
242       dest[i++]= ((cid >> 24) & 0x3F) | 0x80;
243       dest[i++]= ((cid >> 18) & 0x3F) | 0x80;
244       dest[i++]= ((cid >> 12) & 0x3F) | 0x80;
245       dest[i++]= ((cid >>  6) & 0x3F) | 0x80;
246       dest[i++]=  (cid        & 0x3F) | 0x80;
247       dest[i] = '\0';
248       return i;
249     }
250 }
251
252
253 struct CHISE_DS
254 {
255   CHISE_DS_Type type;
256   unsigned char *location;
257   CHISE_NAME_TABLE* feature_names;
258   CHISE_NAME_TABLE* ccs_names;
259   DBTYPE subtype;
260   int modemask;
261 };
262
263 CHISE_DS*
264 chise_open_data_source (CHISE_DS_Type type, char *location,
265                         DBTYPE subtype, int modemask)
266 {
267   CHISE_DS *ds = (CHISE_DS*)malloc (sizeof (CHISE_DS));
268   size_t len = strlen (location);
269
270   if (ds == NULL)
271     return NULL;
272
273   ds->type = type;
274   ds->subtype = subtype;
275   ds->modemask = modemask;
276   ds->location = (unsigned char*)malloc (len + 1);
277   if (ds->location == NULL)
278     {
279       free (ds);
280       return NULL;
281     }
282   strcpy (ds->location, location);
283
284   ds->feature_names = chise_make_name_table ();
285   if (ds->feature_names == NULL)
286     {
287       free (ds->location);
288       free (ds);
289     }
290
291   ds->ccs_names = chise_make_name_table ();
292   if (ds->ccs_names == NULL)
293     {
294       free (ds->feature_names);
295       free (ds->location);
296       free (ds);
297     }
298   return ds;
299 }
300
301 int
302 chise_ds_close (CHISE_DS *ds)
303 {
304   if (ds->location != NULL)
305     free (ds->location);
306   if (ds->feature_names != NULL)
307     free (ds->feature_names);
308   if (ds->ccs_names != NULL)
309     free (ds->ccs_names);
310   free (ds);
311   return 0;
312 }
313
314 CHISE_Feature_Table*
315 chise_ds_get_feature (CHISE_DS *ds, const unsigned char *feature)
316 {
317   CHISE_Feature_Table* ft;
318
319   ft = chise_name_table_get (ds->feature_names, feature);
320   if (ft != NULL)
321     return ft;
322
323   ft = chise_ds_open_feature_table (ds, feature);
324   if (ft == NULL)
325     return NULL;
326
327   if (chise_name_table_put (ds->feature_names, feature, ft))
328     {
329       chise_ft_close (ft);
330       return NULL;
331     }
332   return ft;
333 }
334
335 CHISE_CCS_Table*
336 chise_ds_get_ccs (CHISE_DS *ds, const unsigned char *ccs)
337 {
338   CHISE_CCS_Table* ct;
339
340   ct = chise_name_table_get (ds->ccs_names, ccs);
341   if (ct != NULL)
342     return ct;
343
344   ct = chise_ds_open_ccs_table (ds, ccs);
345   if (ct == NULL)
346     return NULL;
347
348   if (chise_name_table_put (ds->ccs_names, ccs, ct))
349     {
350       chise_ccst_close (ct);
351       return NULL;
352     }
353   return ct;
354 }
355
356
357 struct CHISE_Feature_Table
358 {
359   CHISE_DS *ds;
360   unsigned char *name;
361   CHISE_Attribute_Table *db;
362   u_int32_t access;
363 };
364
365 CHISE_Feature_Table*
366 chise_ds_open_feature_table (CHISE_DS *ds, const char *feature)
367 {
368   CHISE_Feature_Table* table;
369   size_t len = strlen (feature);
370
371   if (ds == NULL)
372     return NULL;
373
374   table = (CHISE_Feature_Table*)malloc (sizeof (CHISE_Feature_Table));
375   if (table == NULL)
376     return NULL;
377
378   table->ds = ds;
379   table->db = NULL;
380   table->access = 0;
381   table->name = (unsigned char*)malloc (len + 1);
382   if (table->name == NULL)
383     {
384       free (table);
385       return NULL;
386     }
387   strcpy (table->name, feature);
388   return table;
389 }
390
391 int
392 chise_ft_close (CHISE_Feature_Table *table)
393 {
394   int status;
395
396   if (table == NULL)
397     return -1;
398
399   if (table->db == NULL)
400     status = -1;
401   else
402     status = chise_close_attribute_table (table->db);
403
404   if (table->name == NULL)
405     status = -1;
406   else
407     {
408       free (table->name);
409       status = 0;
410     }
411   free (table);
412   return status;
413 }
414
415 int
416 chise_char_load_feature_value (CHISE_Char_ID cid,
417                                CHISE_Feature_Table *table,
418                                CHISE_Value *valdatum)
419 {
420   unsigned char key_buf[8];
421
422   if (table->db == NULL)
423     {
424       CHISE_DS *ds = table->ds;
425
426       table->db = chise_open_attribute_table (ds->location,
427                                               "system-char-id", table->name,
428                                               ds->subtype,
429                                               DB_RDONLY, ds->modemask);
430       if (table->db == NULL)
431         return -1;
432       table->access = DB_RDONLY;
433     }
434   chise_format_char_id (cid, key_buf, 8);
435   return chise_get_attribute_table (table->db,
436                                     key_buf, valdatum);
437 }
438
439 unsigned char*
440 chise_char_gets_feature_value (CHISE_Char_ID cid,
441                                CHISE_Feature_Table *table,
442                                unsigned char *buf, size_t size)
443 {
444   CHISE_Value valdatum;
445   unsigned char key_buf[8];
446   int status;
447
448   if (table->db == NULL)
449     {
450       CHISE_DS *ds = table->ds;
451
452       table->db = chise_open_attribute_table (ds->location,
453                                               "system-char-id", table->name,
454                                               ds->subtype,
455                                               DB_RDONLY, ds->modemask);
456       if (table->db == NULL)
457         return NULL;
458       table->access = DB_RDONLY;
459     }
460   chise_format_char_id (cid, key_buf, 8);
461   status = chise_get_attribute_table (table->db,
462                                       key_buf, &valdatum);
463   if (status)
464     return NULL;
465   if (size < valdatum.size)
466     return NULL;
467   strncpy (buf, valdatum.data, valdatum.size);
468   buf[valdatum.size] = '\0';
469   return buf;
470 }
471
472 int
473 chise_char_feature_value_iterate (CHISE_Feature feature,
474                                   int (*func) (CHISE_Char_ID cid,
475                                                CHISE_Feature feature,
476                                                CHISE_Value *valdatum))
477 {
478   DBT keydatum, valdatum;
479   DBC *dbcp;
480   int status;
481
482   if (feature->db == NULL)
483     {
484       CHISE_DS *ds = feature->ds;
485
486       feature->db
487         = chise_open_attribute_table (ds->location,
488                                       "system-char-id", feature->name,
489                                       ds->subtype,
490                                       DB_RDONLY, ds->modemask);
491       if (feature->db == NULL)
492         return -1;
493       feature->access = DB_RDONLY;
494     }
495   xzero (keydatum);
496   xzero (valdatum);
497
498   status = feature->db->cursor (feature->db, NULL, &dbcp, 0);
499   for (status = dbcp->c_get (dbcp, &keydatum, &valdatum, DB_FIRST);
500        status == 0;
501        status = dbcp->c_get (dbcp, &keydatum, &valdatum, DB_NEXT))
502     {
503       unsigned char *key_str = (unsigned char *)keydatum.data;
504       int key_len = strnlen (key_str, keydatum.size);
505       CHISE_Char_ID key = chise_char_id_parse_c_string (key_str, key_len);
506       int ret;
507
508       if (ret = func (key, feature, &valdatum))
509         break;
510     }
511   dbcp->c_close (dbcp);
512   return 0;
513 }
514
515
516 struct CHISE_CCS_Table
517 {
518   CHISE_DS *ds;
519   unsigned char *name;
520   CHISE_Attribute_Table *db;
521   u_int32_t access;
522 };
523
524 CHISE_CCS_Table*
525 chise_ds_open_ccs_table (CHISE_DS *ds, const char *ccs)
526 {
527   CHISE_CCS_Table* table;
528   size_t len = strlen (ccs);
529
530   if (ds == NULL)
531     return NULL;
532
533   table = (CHISE_CCS_Table*)malloc (sizeof (CHISE_CCS_Table));
534   if (table == NULL)
535     return NULL;
536
537   table->ds = ds;
538   table->db = NULL;
539   table->access = 0;
540   table->name = (unsigned char*)malloc (len + 1);
541   if (table->name == NULL)
542     {
543       free (table);
544       return NULL;
545     }
546   strcpy (table->name, ccs);
547   return table;
548 }
549
550 int
551 chise_ccst_close (CHISE_CCS_Table *table)
552 {
553   int status;
554
555   if (table == NULL)
556     return -1;
557
558   if (table->db == NULL)
559     status = 0;
560   else
561     status = chise_close_attribute_table (table->db);
562
563   if (table->name == NULL)
564     status = -1;
565   else
566     {
567       free (table->name);
568       status = 0;
569     }
570   free (table);
571   return status;
572 }
573
574 int
575 chise_ccs_sync (CHISE_CCS ccs)
576 {
577   int status;
578
579   if (ccs->db == NULL)
580     status = 0;
581   else
582     status = chise_close_attribute_table (ccs->db);
583   ccs->db = NULL;
584   ccs->access = 0;
585   return status;
586 }
587
588 CHISE_Char_ID
589 chise_ccs_decode (CHISE_CCS ccs, int code_point)
590 {
591   CHISE_Value valdatum;
592   int status = 0;
593   char key_buf[16];
594
595   if (ccs->db == NULL)
596     {
597       CHISE_DS *ds = ccs->ds;
598
599       ccs->db = chise_open_attribute_table (ds->location,
600                                             ccs->name, "system-char-id",
601                                             ds->subtype,
602                                             DB_RDONLY, ds->modemask);
603       if (ccs->db == NULL)
604         return -1;
605       ccs->access = DB_RDONLY;
606     }
607
608   sprintf(key_buf, "%d", code_point);
609   status = chise_get_attribute_table (ccs->db, key_buf, &valdatum);
610   if (!status)
611     {
612       unsigned char *str
613         = (unsigned char *)chise_value_data (&valdatum);
614       int len = strnlen (str, chise_value_size (&valdatum));
615
616       return chise_char_id_parse_c_string (str, len);
617     }
618   return -1;
619 }
620
621 int
622 chise_ccs_set_decoded_char (CHISE_CCS ccs,
623                             int code_point, CHISE_Char_ID cid)
624 {
625   CHISE_Value valdatum;
626   char key_buf[16], val_buf[8];
627
628   if ((ccs->access & DB_CREATE) == 0)
629     {
630       if (ccs->db != NULL)
631         {
632           chise_close_attribute_table (ccs->db);
633           ccs->db = NULL;
634         }
635       ccs->access = 0;
636     }
637   if (ccs->db == NULL)
638     {
639       CHISE_DS *ds = ccs->ds;
640
641       ccs->db = chise_open_attribute_table (ds->location,
642                                             ccs->name, "system-char-id",
643                                             ds->subtype,
644                                             DB_CREATE, ds->modemask);
645       if (ccs->db == NULL)
646         return -1;
647       ccs->access = DB_CREATE;
648     }
649
650   sprintf(key_buf, "%d", code_point);
651   chise_format_char_id (cid, val_buf, 8);
652   return chise_put_attribute_table (ccs->db, key_buf, val_buf);
653 }
654
655
656 CHISE_Attribute_Table*
657 chise_open_attribute_table (const unsigned char *db_dir,
658                             const char *encoding, const char *feature,
659                             DBTYPE real_subtype,
660                             u_int32_t accessmask, int modemask)
661 {
662   DB* dbase;
663   int status;
664   int len, flen, i;
665   int size;
666   char *db_file_name, *sp;
667
668   status = db_create (&dbase, NULL, 0);
669   if (status)
670     return NULL;
671
672   len = strlen (db_dir);
673   flen = strlen (feature);
674   size = len + strlen (encoding) + flen * 3 + 4;
675   db_file_name = alloca (size);
676   strcpy (db_file_name, db_dir);
677   if (db_file_name[len - 1] != '/')
678     {
679       db_file_name[len++] = '/';
680       db_file_name[len] = '\0';
681     }
682   strcat (db_file_name, encoding);
683   strcat (db_file_name, "/");
684   /* strcat (db_file_name, feature); */
685   sp = &db_file_name[strlen (db_file_name)];
686   for (i = 0; i < flen; i++)
687     {
688       int c = feature[i];
689
690       if ( (c == '/') || (c == '%') )
691         {
692           sprintf (sp, "%%%02X", c);
693           sp += 3;
694         }
695       else
696         *sp++ = c;
697     }
698   *sp = '\0';
699   status = dbase->open (dbase, db_file_name, NULL,
700                         real_subtype, accessmask, modemask);
701   if (status)
702     {
703       dbase->close (dbase, 0);
704       return NULL;
705     }
706   return dbase;
707 }
708
709 int
710 chise_close_attribute_table (CHISE_Attribute_Table *db)
711 {
712   if (db)
713     {
714       db->sync  (db, 0);
715       db->close (db, 0);
716     }
717   return 0;
718 }
719
720 int
721 chise_get_attribute_table (CHISE_Attribute_Table *db,
722                            char *key, CHISE_Value *valdatum)
723 {
724   DBT keydatum;
725   int status = 0;
726
727   /* DB Version 2 requires DBT's to be zeroed before use. */
728   xzero (keydatum);
729   xzero (*valdatum);
730
731   keydatum.data = key;
732   keydatum.size = strlen (key);
733
734   status = db->get (db, NULL, &keydatum, valdatum, 0);
735   return status;
736 }
737
738 int
739 chise_put_attribute_table (CHISE_Attribute_Table *db,
740                            char *key, unsigned char *value)
741 {
742   DBT keydatum, valdatum;
743   int status = 0;
744
745   /* DB Version 2 requires DBT's to be zeroed before use. */
746   xzero (keydatum);
747   xzero (valdatum);
748
749   keydatum.data = key;
750   keydatum.size = strlen (key);
751
752   valdatum.data = value;
753   valdatum.size = strlen (value);
754
755   status = db->put (db, NULL, &keydatum, &valdatum, 0);
756   return status;
757 }