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