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